main
1//
2// Licensed under the terms in License.txt
3//
4// Copyright 2010 Allen Ding. All rights reserved.
5//
6
7#import "KWMatchVerifier.h"
8#import "KWFailure.h"
9#import "KWFormatter.h"
10#import "KWInvocationCapturer.h"
11#import "KWMatcherFactory.h"
12#import "KWReporting.h"
13#import "KWStringUtilities.h"
14#import "KWWorkarounds.h"
15#import "NSInvocation+KiwiAdditions.h"
16#import "NSMethodSignature+KiwiAdditions.h"
17
18@interface KWMatchVerifier()
19
20#pragma mark -
21#pragma mark Properties
22
23@property (nonatomic, readwrite, retain) id<KWMatching> endOfExampleMatcher;
24@property (nonatomic, readwrite, retain) id<KWMatching> matcher;
25
26@end
27
28@implementation KWMatchVerifier
29
30@synthesize matcher;
31
32#pragma mark -
33#pragma mark Initializing
34
35- (id)initForShouldWithCallSite:(KWCallSite *)aCallSite matcherFactory:(KWMatcherFactory *)aMatcherFactory reporter:(id<KWReporting>)aReporter {
36 return [self initWithExpectationType:KWExpectationTypeShould callSite:aCallSite matcherFactory:aMatcherFactory reporter:aReporter];
37}
38
39- (id)initForShouldNotWithCallSite:(KWCallSite *)aCallSite matcherFactory:(KWMatcherFactory *)aMatcherFactory reporter:(id<KWReporting>)aReporter {
40 return [self initWithExpectationType:KWExpectationTypeShouldNot callSite:aCallSite matcherFactory:aMatcherFactory reporter:aReporter];
41}
42
43- (id)initWithExpectationType:(KWExpectationType)anExpectationType callSite:(KWCallSite *)aCallSite matcherFactory:(KWMatcherFactory *)aMatcherFactory reporter:(id<KWReporting>)aReporter {
44 if ((self = [super init])) {
45 expectationType = anExpectationType;
46 callSite = [aCallSite retain];
47 matcherFactory = aMatcherFactory;
48 reporter = aReporter;
49 }
50
51 return self;
52}
53
54+ (id)matchVerifierWithExpectationType:(KWExpectationType)anExpectationType callSite:(KWCallSite *)aCallSite matcherFactory:(KWMatcherFactory *)aMatcherFactory reporter:(id<KWReporting>)aReporter {
55 return [[[self alloc] initWithExpectationType:anExpectationType callSite:aCallSite matcherFactory:aMatcherFactory reporter:aReporter] autorelease];
56}
57
58- (void)dealloc {
59 [subject release];
60 [callSite release];
61 [matcher release];
62 [endOfExampleMatcher release];
63 [super dealloc];
64}
65
66- (NSString *)descriptionForAnonymousItNode
67{
68 NSString *typeString = @"";
69
70 switch (self.expectationType) {
71 case KWExpectationTypeShould:
72 typeString = @"should";
73 break;
74 case KWExpectationTypeShouldNot:
75 typeString = @"should not";
76 }
77 id<KWMatching> actualMatcher = (self.endOfExampleMatcher == nil) ? self.matcher : self.endOfExampleMatcher;
78 return [NSString stringWithFormat:@"%@ %@", typeString, actualMatcher];
79}
80
81#pragma mark -
82#pragma mark Properties
83
84@synthesize expectationType;
85@synthesize callSite;
86@synthesize matcherFactory;
87@synthesize reporter;
88@synthesize subject;
89@synthesize endOfExampleMatcher;
90
91#pragma mark -
92#pragma mark Verifying
93
94- (void)verifyWithMatcher:(id<KWMatching>)aMatcher {
95 @try {
96 BOOL matchResult = [aMatcher evaluate];
97
98 if (self.expectationType == KWExpectationTypeShould && !matchResult) {
99 NSString *message = [aMatcher failureMessageForShould];
100 KWFailure *failure = [KWFailure failureWithCallSite:self.callSite message:message];
101 [self.reporter reportFailure:failure];
102 } else if (self.expectationType == KWExpectationTypeShouldNot && matchResult) {
103 NSString *message = [aMatcher failureMessageForShouldNot];
104 KWFailure *failure = [KWFailure failureWithCallSite:self.callSite message:message];
105 [self.reporter reportFailure:failure];
106 }
107 } @catch (NSException *exception) {
108 KWFailure *failure = [KWFailure failureWithCallSite:self.callSite message:[exception description]];
109 [self.reporter reportFailure:failure];
110 }
111}
112
113#pragma mark -
114#pragma mark Ending Examples
115
116- (void)exampleWillEnd {
117 if (self.endOfExampleMatcher == nil)
118 return;
119
120 [self verifyWithMatcher:self.endOfExampleMatcher];
121}
122
123#pragma mark -
124#pragma mark Handling Invocations
125
126- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
127 NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
128
129 if (signature != nil)
130 return signature;
131
132 signature = [self.matcherFactory methodSignatureForMatcherSelector:aSelector];
133
134 if (signature != nil)
135 return signature;
136
137 // Return a dummy method signature so that problems can be handled in
138 // -forwardInvocation:.
139 NSString *encoding = KWEncodingForVoidMethod();
140 return [NSMethodSignature signatureWithObjCTypes:[encoding UTF8String]];
141}
142
143- (void)forwardInvocation:(NSInvocation *)anInvocation {
144#if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
145 @try {
146#endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
147
148 self.matcher = (id<KWMatching>)[self.matcherFactory matcherFromInvocation:anInvocation subject:self.subject];
149
150 if (self.matcher == nil) {
151 KWFailure *failure = [KWFailure failureWithCallSite:self.callSite format:@"could not create matcher for -%@",
152 NSStringFromSelector(anInvocation.selector)];
153 [self.reporter reportFailure:failure];
154 }
155 [anInvocation invokeWithTarget:self.matcher];
156
157#if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
158 // A matcher might have set an exception within the -invokeWithTarget, so
159 // raise if one was set.
160 NSException *exception = KWGetAndClearExceptionFromAcrossInvocationBoundary();
161 [exception raise];
162#endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
163
164 if ([self.matcher respondsToSelector:@selector(shouldBeEvaluatedAtEndOfExample)] && [self.matcher shouldBeEvaluatedAtEndOfExample]) {
165 self.endOfExampleMatcher = self.matcher;
166 self.matcher = nil;
167 }
168 else {
169 [self verifyWithMatcher:self.matcher];
170 }
171
172#if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
173 } @catch (NSException *exception) {
174 KWFailure *failure = [KWFailure failureWithCallSite:self.callSite format:[exception reason]];
175 [self.reporter reportFailure:failure];
176 return;
177 }
178#endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
179}
180
181@end