Permalink
Browse files

Taking a different approach to how contexts and examples are run.

The problem is that originally, contexts were run from the outside in, starting at the root and working down into nested contexts.

Now, however, each individual example is run one at a time by SenTest, outside of the context run, so we instead need to work out from each example.

Using a bit of clever recursive block behaviour and making KWContext aware of it's parent, we
can work out from an example all the way to the root context, run each beforeAll block just once, then each beforeEach block, traversing back down to the example again, running it and then doing the same for the after blocks.
  • Loading branch information...
1 parent ceda407 commit ee22948f00b864e9a3b186c9612c78dc87818366 @lukeredpath lukeredpath committed Oct 18, 2011
@@ -407,7 +407,7 @@
F5A1E68111743C4A002223E1 /* KWBeWithinMatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWBeWithinMatcher.m; sourceTree = "<group>"; };
F5B168D911BCC58200200D1D /* KWExampleGroupBuilderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWExampleGroupBuilderTest.m; sourceTree = "<group>"; };
F5B1690D11BCDA4100200D1D /* KWContextNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWContextNode.h; sourceTree = "<group>"; };
- F5B1690E11BCDA4100200D1D /* KWContextNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWContextNode.m; sourceTree = "<group>"; };
+ F5B1690E11BCDA4100200D1D /* KWContextNode.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = KWContextNode.m; sourceTree = "<group>"; tabWidth = 4; };
F5B169AD11BCF96E00200D1D /* KWContextNodeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWContextNodeTest.m; sourceTree = "<group>"; };
F5B16A7E11BD350D00200D1D /* KWItNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWItNode.h; sourceTree = "<group>"; };
F5B16A7F11BD350D00200D1D /* KWItNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWItNode.m; sourceTree = "<group>"; };
View
@@ -36,4 +36,9 @@
@property (nonatomic, readonly) KWVoidBlock block;
+#pragma mark -
+#pragma mark Performing blocks
+
+- (void)performBlock;
+
@end
View
@@ -35,6 +35,11 @@ - (void)dealloc {
[super dealloc];
}
+- (void)performBlock
+{
+ if (block != nil) { block(); }
+}
+
#pragma mark -
#pragma mark Getting Call Sites
View
@@ -15,9 +15,11 @@
@class KWItNode;
@class KWPendingNode;
@class KWRegisterMatchersNode;
+@class KWExample;
@interface KWContextNode : NSObject<KWExampleNode> {
@private
+ KWContextNode *parentContext;
KWCallSite *callSite;
NSString *description;
KWRegisterMatchersNode *registerMatchersNode;
@@ -26,14 +28,15 @@
KWBeforeEachNode *beforeEachNode;
KWAfterEachNode *afterEachNode;
NSMutableArray *nodes;
+ BOOL performedExampleCount;
}
#pragma mark -
#pragma mark Initializing
-- (id)initWithCallSite:(KWCallSite *)aCallSite description:(NSString *)aDescription;
+- (id)initWithCallSite:(KWCallSite *)aCallSite parentContext:(KWContextNode *)node description:(NSString *)aDescription;
-+ (id)contextNodeWithCallSite:(KWCallSite *)aCallSite description:(NSString *)aDescription;
++ (id)contextNodeWithCallSite:(KWCallSite *)aCallSite parentContext:(KWContextNode *)contextNode description:(NSString *)aDescription;
#pragma mark -
#pragma mark Getting Call Sites
@@ -59,6 +62,8 @@
- (void)addItNode:(KWItNode *)aNode;
- (void)addPendingNode:(KWPendingNode *)aNode;
+- (void)performExample:(KWExample *)example withBlock:(void (^)(void))exampleBlock;
+
#pragma mark -
#pragma mark Accepting Visitors
View
@@ -6,27 +6,33 @@
#import "KWContextNode.h"
#import "KWExampleNodeVisitor.h"
+#import "KWExample.h"
+#import "KWFailure.h"
@implementation KWContextNode
#pragma mark -
#pragma mark Initializing
-- (id)initWithCallSite:(KWCallSite *)aCallSite description:(NSString *)aDescription {
+- (id)initWithCallSite:(KWCallSite *)aCallSite parentContext:(KWContextNode *)node description:(NSString *)aDescription
+{
if ((self = [super init])) {
+ parentContext = [node retain];
callSite = [aCallSite retain];
description = [aDescription copy];
nodes = [[NSMutableArray alloc] init];
+ performedExampleCount = 0;
}
return self;
}
-+ (id)contextNodeWithCallSite:(KWCallSite *)aCallSite description:(NSString *)aDescription {
- return [[[self alloc] initWithCallSite:aCallSite description:aDescription] autorelease];
++ (id)contextNodeWithCallSite:(KWCallSite *)aCallSite parentContext:(KWContextNode *)contextNode description:(NSString *)aDescription {
+ return [[[self alloc] initWithCallSite:aCallSite parentContext:contextNode description:aDescription] autorelease];
}
- (void)dealloc {
+ [parentContext release];
[callSite release];
[description release];
[registerMatchersNode release];
@@ -91,6 +97,39 @@ - (void)addPendingNode:(KWPendingNode *)aNode {
[(NSMutableArray *)self.nodes addObject:aNode];
}
+- (void)performExample:(KWExample *)example withBlock:(void (^)(void))exampleBlock
+{
+ void (^innerExampleBlock)(void) = [exampleBlock copy];
+
+ void (^outerExampleBlock)(void) = ^{
+ @try {
+ if (performedExampleCount == 0) {
+ [self.registerMatchersNode acceptExampleNodeVisitor:example];
+ [self.beforeAllNode acceptExampleNodeVisitor:example];
+ }
+
+ [self.beforeEachNode acceptExampleNodeVisitor:example];
+
+ innerExampleBlock();
+
+ [self.afterEachNode acceptExampleNodeVisitor:example];
+
+ } @catch (NSException *exception) {
+ KWFailure *failure = [KWFailure failureWithCallSite:self.callSite format:@"%@ \"%@\" raised", [exception name], [exception reason]];
+ [example reportFailure:failure];
+ }
+
+ performedExampleCount++;
+ };
+ if (parentContext == nil) {
+ outerExampleBlock();
+ }
+ else {
+ [parentContext performExample:example withBlock:outerExampleBlock];
+ }
+ [innerExampleBlock release];
+}
+
#pragma mark -
#pragma mark Accepting Visitors
View
@@ -19,11 +19,14 @@
@class KWSpec;
@class KWMatcherFactory;
-@interface KWExample : NSObject <KWExampleNodeVisitor, KWReporting>
+@interface KWExample : NSObject <KWExampleNodeVisitor, KWReporting> {
+ id<KWExampleNode> exampleNode;
+ BOOL passed;
+}
@property (nonatomic, assign) KWExampleSuite *suite;
-- (id)initWithExampleNode:(id<KWExampleNode>)node contextNodeStack:(NSArray *)stack;
+- (id)initWithExampleNode:(id<KWExampleNode>)node;
#pragma mark - Adding Verifiers
View
@@ -35,22 +35,17 @@ @interface KWExample ()
@end
-@implementation KWExample {
- NSArray *contextNodeStack;
- id<KWExampleNode> exampleNode;
- BOOL passed;
-}
+@implementation KWExample
@synthesize matcherFactory;
@synthesize verifiers;
@synthesize exampleNodeStack;
@synthesize delegate = _delegate;
@synthesize suite;
-- (id)initWithExampleNode:(id<KWExampleNode>)node contextNodeStack:(NSArray *)stack;
+- (id)initWithExampleNode:(id<KWExampleNode>)node
{
if ((self = [super init])) {
- contextNodeStack = [stack copy];
exampleNode = [node retain];
matcherFactory = [[KWMatcherFactory alloc] init];
verifiers = [[NSMutableArray alloc] init];
@@ -62,7 +57,6 @@ - (id)initWithExampleNode:(id<KWExampleNode>)node contextNodeStack:(NSArray *)st
- (void)dealloc
{
- [contextNodeStack release];
[exampleNode release];
[exampleNodeStack release];
[matcherFactory release];
@@ -104,7 +98,7 @@ - (void)runWithDelegate:(id<KWExampleDelegate>)delegate;
self.delegate = delegate;
[self.matcherFactory registerMatcherClassesWithNamespacePrefix:@"KW"];
[[KWExampleGroupBuilder sharedExampleGroupBuilder] setCurrentExample:self];
- [[contextNodeStack objectAtIndex:0] acceptExampleNodeVisitor:self];
+ [exampleNode acceptExampleNodeVisitor:self];
}
#pragma mark - Reporting failure
@@ -150,30 +144,22 @@ - (void)reportFailure:(KWFailure *)failure
#pragma mark - Visiting Nodes
-- (void)visitContextNode:(KWContextNode *)aNode {
- [self.exampleNodeStack addObject:aNode];
-
- @try {
- [aNode.registerMatchersNode acceptExampleNodeVisitor:self];
- [aNode.beforeAllNode acceptExampleNodeVisitor:self.suite];
-
- for (id<KWExampleNode> node in aNode.nodes)
- [node acceptExampleNodeVisitor:self];
-
- [aNode.afterAllNode acceptExampleNodeVisitor:self.suite];
- } @catch (NSException *exception) {
- KWFailure *failure = [KWFailure failureWithCallSite:aNode.callSite format:@"%@ \"%@\" raised",
- [exception name],
- [exception reason]];
+- (void)visitRegisterMatchersNode:(KWRegisterMatchersNode *)aNode {
+ [self.matcherFactory registerMatcherClassesWithNamespacePrefix:aNode.namespacePrefix];
+}
- [self reportFailure:failure];
- }
+- (void)visitBeforeAllNode:(KWBeforeAllNode *)aNode {
+ if (aNode.block == nil)
+ return;
- [self.exampleNodeStack removeLastObject];
+ aNode.block();
}
-- (void)visitRegisterMatchersNode:(KWRegisterMatchersNode *)aNode {
- [self.matcherFactory registerMatcherClassesWithNamespacePrefix:aNode.namespacePrefix];
+- (void)visitAfterAllNode:(KWAfterAllNode *)aNode {
+ if (aNode.block == nil)
+ return;
+
+ aNode.block();
}
- (void)visitBeforeEachNode:(KWBeforeEachNode *)aNode {
@@ -196,65 +182,57 @@ - (void)visitItNode:(KWItNode *)aNode {
aNode.example = self;
- @try {
- for (KWContextNode *contextNode in self.exampleNodeStack) {
- if (contextNode.beforeEachNode.block != nil)
- contextNode.beforeEachNode.block();
- }
-
- // Add it node to the stack
- [self.exampleNodeStack addObject:aNode];
-
- @try {
- aNode.block();
-
-#if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
- NSException *invocationException = KWGetAndClearExceptionFromAcrossInvocationBoundary();
- [invocationException raise];
-#endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
+ [aNode.context performExample:self withBlock:^{
+
+ @try {
+ // Add it node to the stack
+ [self.exampleNodeStack addObject:aNode];
- // Finish verifying and clear
- for (id<KWVerifying> verifier in self.verifiers) {
- [verifier exampleWillEnd];
+ @try {
+ aNode.block();
+
+ #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
+ NSException *invocationException = KWGetAndClearExceptionFromAcrossInvocationBoundary();
+ [invocationException raise];
+ #endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
+
+ // Finish verifying and clear
+ for (id<KWVerifying> verifier in self.verifiers) {
+ [verifier exampleWillEnd];
+ }
+
+ } @catch (NSException *exception) {
+ KWFailure *failure = [KWFailure failureWithCallSite:aNode.callSite format:@"%@ \"%@\" raised",
+ [exception name],
+ [exception reason]];
+ [self reportFailure:failure];
}
-
- } @catch (NSException *exception) {
+
+ // Remove it node from the stack
+ [self.exampleNodeStack removeLastObject];
+
+ } @catch (NSException *exception) {
KWFailure *failure = [KWFailure failureWithCallSite:aNode.callSite format:@"%@ \"%@\" raised",
[exception name],
[exception reason]];
[self reportFailure:failure];
}
-
- // Remove it node from the stack
- [self.exampleNodeStack removeLastObject];
- for (KWContextNode *contextNode in self.exampleNodeStack) {
- if (contextNode.afterEachNode.block != nil)
- contextNode.afterEachNode.block();
+ if (passed) {
+ NSLog(@"+ '%@ %@' [PASSED]", [self descriptionForExampleContext], [exampleNode description]);
}
- } @catch (NSException *exception) {
- KWFailure *failure = [KWFailure failureWithCallSite:aNode.callSite format:@"%@ \"%@\" raised",
- [exception name],
- [exception reason]];
- [self reportFailure:failure];
- }
-
- if (passed) {
- NSLog(@"+ '%@ %@' [PASSED]", [self descriptionForExampleContext], [exampleNode description]);
- }
-
- // Always clear stubs and spies at the end of it blocks
- KWClearAllMessageSpies();
- KWClearAllObjectStubs();
+
+ // Always clear stubs and spies at the end of it blocks
+ KWClearAllMessageSpies();
+ KWClearAllObjectStubs();
+ }];
}
- (void)visitPendingNode:(KWPendingNode *)aNode {
if (aNode != exampleNode)
return;
- [self.exampleNodeStack addObject:aNode];
NSLog(@"+ '%@' [PENDING]", [self descriptionForExampleContext]);
- [self.exampleNodeStack removeLastObject];
}
- (NSString *)generateDescriptionForAnonymousItNode
@@ -95,8 +95,8 @@ - (BOOL)isBuildingExampleGroup {
- (KWExampleSuite *)buildExampleGroups:(void (^)(void))buildingBlock
{
- KWContextNode *rootNode = [KWContextNode contextNodeWithCallSite:nil description:nil];
-
+ KWContextNode *rootNode = [KWContextNode contextNodeWithCallSite:nil parentContext:nil description:nil];
+
self.exampleSuite = [[[KWExampleSuite alloc] initWithRootNode:rootNode] autorelease];
[suites addObject:self.exampleSuite];
@@ -120,7 +120,7 @@ - (KWExampleSuite *)buildExampleGroups:(void (^)(void))buildingBlock
- (void)pushContextNodeWithCallSite:(KWCallSite *)aCallSite description:(NSString *)aDescription {
KWContextNode *contextNode = [self.contextNodeStack lastObject];
- KWContextNode *node = [KWContextNode contextNodeWithCallSite:aCallSite description:aDescription];
+ KWContextNode *node = [KWContextNode contextNodeWithCallSite:aCallSite parentContext:contextNode description:aDescription];
[contextNode addContextNode:node];
[self.contextNodeStack addObject:node];
}
@@ -182,10 +182,10 @@ - (void)addItNodeWithCallSite:(KWCallSite *)aCallSite description:(NSString *)aD
[NSException raise:@"KWExampleGroupBuilderException" format:@"an example group has not been started"];
KWContextNode *contextNode = [self.contextNodeStack lastObject];
- KWItNode* itNode = [KWItNode itNodeWithCallSite:aCallSite description:aDescription block:aBlock];
+ KWItNode* itNode = [KWItNode itNodeWithCallSite:aCallSite description:aDescription context:contextNode block:aBlock];
[contextNode addItNode:itNode];
- KWExample *example = [[KWExample alloc] initWithExampleNode:itNode contextNodeStack:self.contextNodeStack];
+ KWExample *example = [[KWExample alloc] initWithExampleNode:itNode];
[self.exampleSuite addExample:example];
[example release];
}
@@ -198,7 +198,7 @@ - (void)addPendingNodeWithCallSite:(KWCallSite *)aCallSite description:(NSString
KWPendingNode *pendingNode = [KWPendingNode pendingNodeWithCallSite:aCallSite description:aDescription];
[contextNode addPendingNode:pendingNode];
- KWExample *example = [[KWExample alloc] initWithExampleNode:pendingNode contextNodeStack:self.contextNodeStack];
+ KWExample *example = [[KWExample alloc] initWithExampleNode:pendingNode];
[self.exampleSuite addExample:example];
[example release];
}
View
@@ -16,7 +16,6 @@
@interface KWExampleSuite : NSObject <KWExampleNodeVisitor> {
KWContextNode *rootNode;
NSMutableSet *examples;
- NSMutableSet *visitedNodes;
}
- (id)initWithRootNode:(KWContextNode *)contextNode;
- (void)addExample:(KWExample *)example;
Oops, something went wrong.

0 comments on commit ee22948

Please sign in to comment.