Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
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...
commit ee22948f00b864e9a3b186c9612c78dc87818366 1 parent ceda407
@lukeredpath lukeredpath authored
View
2  Kiwi.xcodeproj/project.pbxproj
@@ -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
5 Kiwi/KWBlockNode.h
@@ -36,4 +36,9 @@
@property (nonatomic, readonly) KWVoidBlock block;
+#pragma mark -
+#pragma mark Performing blocks
+
+- (void)performBlock;
+
@end
View
5 Kiwi/KWBlockNode.m
@@ -35,6 +35,11 @@ - (void)dealloc {
[super dealloc];
}
+- (void)performBlock
+{
+ if (block != nil) { block(); }
+}
+
#pragma mark -
#pragma mark Getting Call Sites
View
9 Kiwi/KWContextNode.h
@@ -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
45 Kiwi/KWContextNode.m
@@ -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
7 Kiwi/KWExample.h
@@ -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
122 Kiwi/KWExample.m
@@ -35,11 +35,7 @@ @interface KWExample ()
@end
-@implementation KWExample {
- NSArray *contextNodeStack;
- id<KWExampleNode> exampleNode;
- BOOL passed;
-}
+@implementation KWExample
@synthesize matcherFactory;
@synthesize verifiers;
@@ -47,10 +43,9 @@ @implementation KWExample {
@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
View
12 Kiwi/KWExampleGroupBuilder.m
@@ -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
1  Kiwi/KWExampleSuite.h
@@ -16,7 +16,6 @@
@interface KWExampleSuite : NSObject <KWExampleNodeVisitor> {
KWContextNode *rootNode;
NSMutableSet *examples;
- NSMutableSet *visitedNodes;
}
- (id)initWithRootNode:(KWContextNode *)contextNode;
- (void)addExample:(KWExample *)example;
View
22 Kiwi/KWExampleSuite.m
@@ -23,14 +23,12 @@ - (id)initWithRootNode:(KWContextNode *)contextNode
if ((self = [super init])) {
rootNode = [contextNode retain];
examples = [[NSMutableSet alloc] init];
- visitedNodes = [[NSMutableSet alloc] init];
}
return self;
}
- (void)dealloc
{
- [visitedNodes release];
[examples release];
[rootNode release];
[super dealloc];
@@ -58,26 +56,6 @@ - (NSArray *)invocationsForTestCase;
return invocations;
}
-#pragma mark - Node visiting
-
-- (void)visitBeforeAllNode:(KWBeforeAllNode *)aNode {
- if (aNode.block == nil || [visitedNodes containsObject:aNode])
- return;
-
- [visitedNodes addObject:aNode];
-
- aNode.block();
-}
-
-- (void)visitAfterAllNode:(KWAfterAllNode *)aNode {
- if (aNode.block == nil || [visitedNodes containsObject:aNode])
- return;
-
- [visitedNodes addObject:aNode];
-
- aNode.block();
-}
-
@end
#pragma mark -
View
11 Kiwi/KWItNode.h
@@ -10,14 +10,21 @@
@class KWPendingNode;
@class KWExample;
+@class KWContextNode;
-@interface KWItNode : KWBlockNode<KWExampleNode>
+@interface KWItNode : KWBlockNode<KWExampleNode> {
+ KWContextNode *context;
+}
@property (nonatomic, assign) KWExample *example;
+@property (nonatomic, retain, readonly) KWContextNode *context;
#pragma mark -
#pragma mark Initializing
-+ (id)itNodeWithCallSite:(KWCallSite *)aCallSite description:(NSString *)aDescription block:(KWVoidBlock)aBlock;
++ (id)itNodeWithCallSite:(KWCallSite *)aCallSite
+ description:(NSString *)aDescription
+ context:(KWContextNode *)context
+ block:(KWVoidBlock)aBlock;
@end
View
17 Kiwi/KWItNode.m
@@ -9,15 +9,28 @@
#import "KWExample.h"
#import "KWVerifying.h"
+@interface KWItNode ()
+
+@property (nonatomic, retain, readwrite) KWContextNode *context;
+
+@end
+
@implementation KWItNode
+@synthesize context;
@synthesize example;
#pragma mark -
#pragma mark Initializing
-+ (id)itNodeWithCallSite:(KWCallSite *)aCallSite description:(NSString *)aDescription block:(KWVoidBlock)aBlock {
- return [[[self alloc] initWithCallSite:aCallSite description:aDescription block:aBlock] autorelease];
++ (id)itNodeWithCallSite:(KWCallSite *)aCallSite
+ description:(NSString *)aDescription
+ context:(KWContextNode *)context
+ block:(KWVoidBlock)aBlock;
+{
+ KWItNode *itNode = [[self alloc] initWithCallSite:aCallSite description:aDescription block:aBlock];
+ itNode.context = context;
+ return [itNode autorelease];
}
#pragma mark -

0 comments on commit ee22948

Please sign in to comment.
Something went wrong with that request. Please try again.