Skip to content

Commit

Permalink
Allow multiple registerMatchers in one context.
Browse files Browse the repository at this point in the history
The old implementation appeared to mimic that of `beforeEach` and
`afterEach`--it only allowed one per context. However, there is no need
for such a restriction when registering custom matchers. This commit
allows any number of matchers to be registered in a single context.

Other changes:

- Remove obsolete forward class declarations from KWExample header.
- Delete commented out code from KWMatcherFactory.
- Consolidate duplicated code in KWExample, KWContext in internal methods.
- Add missing variable name from block typedef (only used in
  `defineMatcher` macro).
  • Loading branch information
modocache committed Feb 11, 2014
1 parent af20492 commit 1afae44
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 55 deletions.
2 changes: 0 additions & 2 deletions Classes/Core/KWExample.h
Expand Up @@ -17,8 +17,6 @@
@class KWCallSite;
@class KWExampleSuite;
@class KWContextNode;
@class KWSpec;
@class KWMatcherFactory;

@interface KWExample : NSObject <KWExampleNodeVisitor, KWReporting>

Expand Down
41 changes: 22 additions & 19 deletions Classes/Core/KWExampleSuiteBuilder.m
Expand Up @@ -20,6 +20,8 @@
#import "KWRegisterMatchersNode.h"
#import "KWSymbolicator.h"

static NSString * const KWExampleSuiteBuilderException = @"KWExampleSuiteBuilderException";

@interface KWExampleSuiteBuilder()

#pragma mark - Building Example Groups
Expand Down Expand Up @@ -133,68 +135,63 @@ - (void)popContextNode {

[self.currentExampleSuite markLastExampleAsLastInContext:contextNode];

if ([self.contextNodeStack count] == 1)
[NSException raise:@"KWExampleSuiteBuilderException" format:@"there is no open context to pop"];
if ([self.contextNodeStack count] == 1) {
[NSException raise:KWExampleSuiteBuilderException
format:@"there is no open context to pop"];
}

[self.contextNodeStack removeLastObject];
}

- (void)setRegisterMatchersNodeWithCallSite:(KWCallSite *)aCallSite namespacePrefix:(NSString *)aNamespacePrefix {
if ([self.contextNodeStack count] == 0)
[NSException raise:@"KWExampleSuiteBuilderException" format:@"an example group has not been started"];
[self raiseIfExampleGroupNotStarted];

KWContextNode *contextNode = [self.contextNodeStack lastObject];
KWRegisterMatchersNode *registerMatchersNode = [KWRegisterMatchersNode registerMatchersNodeWithCallSite:aCallSite namespacePrefix:aNamespacePrefix];
[contextNode setRegisterMatchersNode:registerMatchersNode];
[contextNode addRegisterMatchersNode:registerMatchersNode];
}

- (void)setBeforeAllNodeWithCallSite:(KWCallSite *)aCallSite block:(void (^)(void))block {
if ([self.contextNodeStack count] == 0)
[NSException raise:@"KWExampleSuiteBuilderException" format:@"an example group has not been started"];
[self raiseIfExampleGroupNotStarted];

KWContextNode *contextNode = [self.contextNodeStack lastObject];
KWBeforeAllNode *beforeAllNode = [KWBeforeAllNode beforeAllNodeWithCallSite:aCallSite block:block];
[contextNode setBeforeAllNode:beforeAllNode];
}

- (void)setAfterAllNodeWithCallSite:(KWCallSite *)aCallSite block:(void (^)(void))block {
if ([self.contextNodeStack count] == 0)
[NSException raise:@"KWExampleSuiteBuilderException" format:@"an example group has not been started"];
[self raiseIfExampleGroupNotStarted];

KWContextNode *contextNode = [self.contextNodeStack lastObject];
KWAfterAllNode *afterAllNode = [KWAfterAllNode afterAllNodeWithCallSite:aCallSite block:block];
[contextNode setAfterAllNode:afterAllNode];
}

- (void)setBeforeEachNodeWithCallSite:(KWCallSite *)aCallSite block:(void (^)(void))block {
if ([self.contextNodeStack count] == 0)
[NSException raise:@"KWExampleSuiteBuilderException" format:@"an example group has not been started"];
[self raiseIfExampleGroupNotStarted];

KWContextNode *contextNode = [self.contextNodeStack lastObject];
KWBeforeEachNode *beforeEachNode = [KWBeforeEachNode beforeEachNodeWithCallSite:aCallSite block:block];
[contextNode setBeforeEachNode:beforeEachNode];
}

- (void)setAfterEachNodeWithCallSite:(KWCallSite *)aCallSite block:(void (^)(void))block {
if ([self.contextNodeStack count] == 0)
[NSException raise:@"KWExampleSuiteBuilderException" format:@"an example group has not been started"];
[self raiseIfExampleGroupNotStarted];

KWContextNode *contextNode = [self.contextNodeStack lastObject];
KWAfterEachNode *afterEachNode = [KWAfterEachNode afterEachNodeWithCallSite:aCallSite block:block];
[contextNode setAfterEachNode:afterEachNode];
}

- (void)addLetNodeWithCallSite:(KWCallSite *)aCallSite objectRef:(__autoreleasing id *)anObjectRef symbolName:(NSString *)aSymbolName block:(id (^)(void))block {
if ([self.contextNodeStack count] == 0)
[NSException raise:@"KWExampleSuiteBuilderException" format:@"an example group has not been started"];
[self raiseIfExampleGroupNotStarted];

KWContextNode *contextNode = [self.contextNodeStack lastObject];
[contextNode addLetNode:[KWLetNode letNodeWithSymbolName:aSymbolName objectRef:anObjectRef block:block]];
}

- (void)addItNodeWithCallSite:(KWCallSite *)aCallSite description:(NSString *)aDescription block:(void (^)(void))block {
if ([self.contextNodeStack count] == 0)
[NSException raise:@"KWExampleSuiteBuilderException" format:@"an example group has not been started"];
[self raiseIfExampleGroupNotStarted];

KWContextNode *contextNode = [self.contextNodeStack lastObject];

Expand All @@ -221,8 +218,7 @@ - (BOOL)shouldAddItNodeWithCallSite:(KWCallSite *)aCallSite toContextNode:(KWCon
}

- (void)addPendingNodeWithCallSite:(KWCallSite *)aCallSite description:(NSString *)aDescription {
if ([self.contextNodeStack count] == 0)
[NSException raise:@"KWExampleSuiteBuilderException" format:@"an example group has not been started"];
[self raiseIfExampleGroupNotStarted];

KWContextNode *contextNode = [self.contextNodeStack lastObject];
KWPendingNode *pendingNode = [KWPendingNode pendingNodeWithCallSite:aCallSite context:contextNode description:aDescription];
Expand All @@ -231,4 +227,11 @@ - (void)addPendingNodeWithCallSite:(KWCallSite *)aCallSite description:(NSString
[self.currentExampleSuite addExample:example];
}

- (void)raiseIfExampleGroupNotStarted {
if ([self.contextNodeStack count] == 0) {
[NSException raise:KWExampleSuiteBuilderException
format:@"an example group has not been started"];
}
}

@end
4 changes: 0 additions & 4 deletions Classes/Core/KWMatcherFactory.h
Expand Up @@ -26,10 +26,6 @@
- (void)registerMatcherClass:(Class)aClass;
- (void)registerMatcherClassesWithNamespacePrefix:(NSString *)aNamespacePrefix;

#pragma mark - Registering User Defined Matchers

//- (void)registerUserDefinedMatcherWithBuilder:(KWUserDefinedMatcherBuilder *)aBuilder;

#pragma mark - Getting Method Signatures

- (NSMethodSignature *)methodSignatureForMatcherSelector:(SEL)aSelector;
Expand Down
7 changes: 0 additions & 7 deletions Classes/Core/KWMatcherFactory.m
Expand Up @@ -90,13 +90,6 @@ - (void)registerMatcherClassesWithNamespacePrefix:(NSString *)aNamespacePrefix {
}
}

#pragma mark - Registering User Defined Matchers

//- (void)registerUserDefinedMatcherWithBuilder:(KWUserDefinedMatcherBuilder *)aBuilder
//{
//
//}

#pragma mark - Getting Method Signatures

- (NSMethodSignature *)methodSignatureForMatcherSelector:(SEL)aSelector {
Expand Down
2 changes: 1 addition & 1 deletion Classes/Core/KWMatchers.h
Expand Up @@ -10,7 +10,7 @@

@class KWUserDefinedMatcherBuilder;

typedef void (^KWMatchersBuildingBlock)(KWUserDefinedMatcherBuilder *);
typedef void (^KWMatchersBuildingBlock)(KWUserDefinedMatcherBuilder *matcherBuilder);

@class KWUserDefinedMatcher;

Expand Down
3 changes: 2 additions & 1 deletion Classes/Nodes/KWContextNode.h
Expand Up @@ -36,12 +36,12 @@

#pragma mark - Managing Nodes

@property (nonatomic, strong) KWRegisterMatchersNode *registerMatchersNode;
@property (nonatomic, strong) KWBeforeAllNode *beforeAllNode;
@property (nonatomic, strong) KWAfterAllNode *afterAllNode;
@property (nonatomic, strong) KWBeforeEachNode *beforeEachNode;
@property (nonatomic, strong) KWAfterEachNode *afterEachNode;
@property (nonatomic, readonly) NSArray *nodes;
@property (nonatomic, readonly) NSArray *registerMatchersNodes;
@property (nonatomic, readonly) NSArray *letNodes;

@property (nonatomic, readonly) KWContextNode *parentContext;
Expand All @@ -50,6 +50,7 @@

- (void)addContextNode:(KWContextNode *)aNode;
- (void)addLetNode:(KWLetNode *)aNode;
- (void)addRegisterMatchersNode:(KWRegisterMatchersNode *)aNode;
- (void)addItNode:(KWItNode *)aNode;
- (void)addPendingNode:(KWPendingNode *)aNode;

Expand Down
45 changes: 24 additions & 21 deletions Classes/Nodes/KWContextNode.m
Expand Up @@ -17,6 +17,8 @@
#import "KWRegisterMatchersNode.h"
#import "KWSymbolicator.h"

static NSString * const KWContextNodeException = @"KWContextNodeException";

@interface KWContextNode()

@property (nonatomic, assign) NSUInteger performedExampleCount;
Expand All @@ -33,8 +35,9 @@ - (id)initWithCallSite:(KWCallSite *)aCallSite parentContext:(KWContextNode *)no
_parentContext = node;
_callSite = aCallSite;
_description = [aDescription copy];
_nodes = [[NSMutableArray alloc] init];
_letNodes = [[NSMutableArray alloc] init];
_nodes = [NSMutableArray array];
_registerMatchersNodes = [NSMutableArray array];
_letNodes = [NSMutableArray array];
_performedExampleCount = 0;
}

Expand All @@ -49,34 +52,25 @@ - (void)addContextNode:(KWContextNode *)aNode {
[(NSMutableArray *)self.nodes addObject:aNode];
}

- (void)setRegisterMatchersNode:(KWRegisterMatchersNode *)aNode {
if (self.registerMatchersNode != nil)
[NSException raise:@"KWContextNodeException" format:@"a register matchers node already exists"];

_registerMatchersNode = aNode;
}

- (void)setBeforeEachNode:(KWBeforeEachNode *)aNode {
if (self.beforeEachNode != nil)
[NSException raise:@"KWContextNodeException" format:@"a before each node already exists"];

[self raiseIfNodeAlreadyExists:self.beforeEachNode];
_beforeEachNode = aNode;
}

- (void)setAfterEachNode:(KWAfterEachNode *)aNode {
if (self.afterEachNode != nil)
[NSException raise:@"KWContextNodeException" format:@"an after each node already exists"];

[self raiseIfNodeAlreadyExists:self.afterEachNode];
_afterEachNode = aNode;
}

- (void)addLetNode:(KWLetNode *)aNode
{
- (void)addLetNode:(KWLetNode *)aNode {
[(NSMutableArray *)self.letNodes addObject:aNode];
}

- (KWLetNode *)letNodeTree
{
- (void)addRegisterMatchersNode:(KWRegisterMatchersNode *)aNode {
[(NSMutableArray *)self.registerMatchersNodes addObject:aNode];
}

- (KWLetNode *)letNodeTree {
KWLetNode *tree = [self.parentContext letNodeTree];
for (KWLetNode *letNode in self.letNodes) {
if (!tree) {
Expand All @@ -103,8 +97,10 @@ - (void)performExample:(KWExample *)example withBlock:(void (^)(void))exampleBlo

void (^outerExampleBlock)(void) = ^{
@try {
[self.registerMatchersNode acceptExampleNodeVisitor:example];

for (KWRegisterMatchersNode *registerNode in self.registerMatchersNodes) {
[registerNode acceptExampleNodeVisitor:example];
}

if (self.performedExampleCount == 0) {
[self.beforeAllNode acceptExampleNodeVisitor:example];
}
Expand Down Expand Up @@ -138,6 +134,13 @@ - (void)performExample:(KWExample *)example withBlock:(void (^)(void))exampleBlo
}
}

- (void)raiseIfNodeAlreadyExists:(id<KWExampleNode>)node {
if (node) {
[NSException raise:KWContextNodeException
format:@"A %@ already exists in this context.", NSStringFromClass([node class])];
}
}

#pragma mark - Accepting Visitors

- (void)acceptExampleNodeVisitor:(id<KWExampleNodeVisitor>)aVisitor {
Expand Down
4 changes: 4 additions & 0 deletions Kiwi.xcodeproj/project.pbxproj
Expand Up @@ -778,6 +778,7 @@
DA084E6C17E3838100592D5A /* KWRegularExpressionPatternMatcher.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4E3C5DB01716C34900835B62 /* KWRegularExpressionPatternMatcher.h */; };
DA084E6D17E3838100592D5A /* KWSymbolicator.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C533F7D117462CAA000CAB02 /* KWSymbolicator.h */; };
DA084E6E17E3838100592D5A /* NSProxy+KiwiVerifierAdditions.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9F820DB616BB6748003A1BA5 /* NSProxy+KiwiVerifierAdditions.h */; };
DA3EAD0A18A86E5600EBBF57 /* KWUserDefinedMatcherFunctionalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3EAD0918A86E5600EBBF57 /* KWUserDefinedMatcherFunctionalTest.m */; };
DA92B2A617E6A3EF0062F84D /* SenTestSuite+KiwiAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA92B2A417E6A3EF0062F84D /* SenTestSuite+KiwiAdditions.h */; };
DA92B2A717E6A3EF0062F84D /* SenTestSuite+KiwiAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA92B2A517E6A3EF0062F84D /* SenTestSuite+KiwiAdditions.m */; };
DAA35A2317E3CF6600C41AE2 /* libKiwi-XCTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA084E7317E3838100592D5A /* libKiwi-XCTest.a */; };
Expand Down Expand Up @@ -1273,6 +1274,7 @@
DA084D5317E381C700592D5A /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
DA084D5A17E3831E00592D5A /* KWXCFunctionalTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWXCFunctionalTest.m; sourceTree = "<group>"; };
DA084E7317E3838100592D5A /* libKiwi-XCTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libKiwi-XCTest.a"; sourceTree = BUILT_PRODUCTS_DIR; };
DA3EAD0918A86E5600EBBF57 /* KWUserDefinedMatcherFunctionalTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWUserDefinedMatcherFunctionalTest.m; sourceTree = "<group>"; };
DA92B2A417E6A3EF0062F84D /* SenTestSuite+KiwiAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SenTestSuite+KiwiAdditions.h"; sourceTree = "<group>"; };
DA92B2A517E6A3EF0062F84D /* SenTestSuite+KiwiAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SenTestSuite+KiwiAdditions.m"; sourceTree = "<group>"; };
DAAC61CA17E75B50000165F6 /* KWObjCUtilitiesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWObjCUtilitiesTest.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1810,6 +1812,7 @@
4E3C5DB71716C68000835B62 /* KWRegularExpressionPatternMatcherTest.m */,
F553B6641175D48C004FCA2E /* KWRespondToSelectorMatcherTest.m */,
A385CAE713AA7EA200DCA951 /* KWUserDefinedMatcherTest.m */,
DA3EAD0918A86E5600EBBF57 /* KWUserDefinedMatcherFunctionalTest.m */,
);
name = Matchers;
sourceTree = "<group>";
Expand Down Expand Up @@ -2636,6 +2639,7 @@
buildActionMask = 2147483647;
files = (
F53B5E43115B33FC0022BC0B /* KWContainMatcherTest.m in Sources */,
DA3EAD0A18A86E5600EBBF57 /* KWUserDefinedMatcherFunctionalTest.m in Sources */,
F53B5E66115B36EB0022BC0B /* KWEqualMatcherTest.m in Sources */,
F53B5EC6115B3D180022BC0B /* KWRaiseMatcherTest.m in Sources */,
F5C36E93115C9F0700425FDA /* KWReceiveMatcherTest.m in Sources */,
Expand Down
19 changes: 19 additions & 0 deletions Tests/KWContextNodeTest.m
Expand Up @@ -33,6 +33,25 @@ - (void)testItShouldAllowOnlyOneAfterEachBlock {
STAssertThrows([node setAfterEachNode:[KWAfterEachNode afterEachNodeWithCallSite:nil block:block]], @"expected exception");
}

#pragma mark - Context registerMatchers nodes

- (void)testItAddsNewRegisterMatchersNodesToAnArray {
KWRegisterMatchersNode *nodeAbc = [[KWRegisterMatchersNode alloc] initWithCallSite:nil
namespacePrefix:@"ABC"];
KWRegisterMatchersNode *nodeXyz = [[KWRegisterMatchersNode alloc] initWithCallSite:nil
namespacePrefix:@"XYZ"];
KWContextNode *context = [KWContextNode contextNodeWithCallSite:nil
parentContext:nil
description:nil];
[context addRegisterMatchersNode:nodeAbc];
[context addRegisterMatchersNode:nodeXyz];

NSArray *expectedRegisterMatchersNodes = @[nodeAbc, nodeXyz];
STAssertEqualObjects(context.registerMatchersNodes,
expectedRegisterMatchersNodes,
@"register matchers nodes not stored in the order they were added");
}

#pragma mark - Context let nodes

- (void)testItAddsNewLetNodesToAnArray {
Expand Down
71 changes: 71 additions & 0 deletions Tests/KWUserDefinedMatcherFunctionalTest.m
@@ -0,0 +1,71 @@
//
// Licensed under the terms in License.txt
//
// Copyright 2014 Allen Ding. All rights reserved.
//

#import "Kiwi.h"
#import "Cruiser.h"

#pragma mark - Custom Matcher Implementations

@interface HALTHyperdriveLevelMatcher : KWMatcher
@property (nonatomic, assign) NSInteger hyperdriveFuelLevel;
@end

@implementation HALTHyperdriveLevelMatcher

+ (NSArray *)matcherStrings {
return @[@"haveHyperdriveFuelLevelGreaterThan:"];
}

- (BOOL)evaluate {
return ((Cruiser *)self.subject).hyperdriveFuelLevel > self.hyperdriveFuelLevel;
}

- (void)haveHyperdriveFuelLevelGreaterThan:(NSInteger)hyperdriveFuelLevel {
self.hyperdriveFuelLevel = hyperdriveFuelLevel;
}

@end

@interface FLOWCrewComplementMatcher : KWMatcher
@property (nonatomic, assign) NSInteger crewComplement;
@end

@implementation FLOWCrewComplementMatcher

+ (NSArray *)matcherStrings {
return @[@"haveCrewComplementLessThan:"];
}

- (BOOL)evaluate {
return ((Cruiser *)self.subject).crewComplement < self.crewComplement;
}

- (void)haveCrewComplementLessThan:(NSInteger)crewComplement {
self.crewComplement = crewComplement;
}

@end

#pragma mark - Tests

SPEC_BEGIN(KWUserDefinedMatcherFunctionalTest)

registerMatchers(@"HALT");
registerMatchers(@"FLOW");

describe(@"Cruiser", ^{
let(cruiser, ^id{ return [Cruiser new]; });

it(@"has a hyperdrive fuel level above 50", ^{
[[cruiser should] haveHyperdriveFuelLevelGreaterThan:50];
});

it(@"has a crew complement below 5000", ^{
[[cruiser should] haveCrewComplementLessThan:5000];
});
});

SPEC_END

0 comments on commit 1afae44

Please sign in to comment.