main
1//
2// Licensed under the terms in License.txt
3//
4// Copyright 2010 Allen Ding. All rights reserved.
5//
6
7#import "KWReceiveMatcher.h"
8#import "KWFormatter.h"
9#import "KWInvocationCapturer.h"
10#import "KWMessagePattern.h"
11#import "KWMessageTracker.h"
12#import "KWObjCUtilities.h"
13#import "KWStringUtilities.h"
14#import "KWWorkarounds.h"
15#import "NSObject+KiwiStubAdditions.h"
16
17static NSString * const MatchVerifierKey = @"MatchVerifierKey";
18static NSString * const CountTypeKey = @"CountTypeKey";
19static NSString * const CountKey = @"CountKey";
20static NSString * const StubValueKey = @"StubValueKey";
21
22@interface KWReceiveMatcher()
23
24#pragma mark -
25#pragma mark Properties
26
27@property (nonatomic, readwrite, retain) KWMessageTracker *messageTracker;
28
29@end
30
31@implementation KWReceiveMatcher
32
33#pragma mark -
34#pragma mark Initializing
35
36- (id)initWithSubject:(id)anObject {
37 if ((self = [super initWithSubject:anObject])) {
38 self.willEvaluateMultipleTimes = NO;
39 }
40
41 return self;
42}
43
44- (void)dealloc {
45 [messageTracker release];
46 [super dealloc];
47}
48
49#pragma mark -
50#pragma mark Properties
51
52@synthesize messageTracker;
53@synthesize willEvaluateMultipleTimes;
54
55#pragma mark -
56#pragma mark Getting Matcher Strings
57
58+ (NSArray *)matcherStrings {
59 return @[@"receive:",
60 @"receive:withCount:",
61 @"receive:withCountAtLeast:",
62 @"receive:withCountAtMost:",
63 @"receive:andReturn:",
64 @"receive:andReturn:withCount:",
65 @"receive:andReturn:withCountAtLeast:",
66 @"receive:andReturn:withCountAtMost:",
67 @"receiveMessagePattern:countType:count:",
68 @"receiveMessagePattern:andReturn:countType:count:"];
69}
70
71#pragma mark -
72#pragma mark Matching
73
74- (BOOL)shouldBeEvaluatedAtEndOfExample {
75 return YES;
76}
77
78- (BOOL)evaluate {
79 BOOL succeeded = [self.messageTracker succeeded];
80
81 if (!self.willEvaluateMultipleTimes) {
82 [self.messageTracker stopTracking];
83 }
84 return succeeded;
85}
86
87#pragma mark -
88#pragma mark Getting Failure Messages
89
90- (NSString *)failureMessageForShould {
91 return [NSString stringWithFormat:@"expected subject to receive -%@ %@, but received it %@",
92 [self.messageTracker.messagePattern stringValue],
93 [self.messageTracker expectedCountPhrase],
94 [self.messageTracker receivedCountPhrase]];
95}
96
97- (NSString *)failureMessageForShouldNot {
98 return [NSString stringWithFormat:@"expected subject not to receive -%@, but received it %@",
99 [self.messageTracker.messagePattern stringValue],
100 [self.messageTracker receivedCountPhrase]];
101}
102
103#pragma mark -
104#pragma mark Configuring Matchers
105
106- (void)receive:(SEL)aSelector {
107 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
108 [self receiveMessagePattern:messagePattern countType:KWCountTypeExact count:1];
109}
110
111- (void)receive:(SEL)aSelector withCount:(NSUInteger)aCount {
112 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
113 return [self receiveMessagePattern:messagePattern countType:KWCountTypeExact count:aCount];
114}
115
116- (void)receive:(SEL)aSelector withCountAtLeast:(NSUInteger)aCount {
117 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
118 return [self receiveMessagePattern:messagePattern countType:KWCountTypeAtLeast count:aCount];
119}
120
121- (void)receive:(SEL)aSelector withCountAtMost:(NSUInteger)aCount {
122 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
123 return [self receiveMessagePattern:messagePattern countType:KWCountTypeAtMost count:aCount];
124}
125
126- (void)receive:(SEL)aSelector andReturn:(id)aValue {
127 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
128 [self receiveMessagePattern:messagePattern andReturn:aValue countType:KWCountTypeExact count:1];
129}
130
131- (void)receive:(SEL)aSelector andReturn:(id)aValue withCount:(NSUInteger)aCount {
132 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
133 [self receiveMessagePattern:messagePattern andReturn:aValue countType:KWCountTypeExact count:aCount];
134}
135
136- (void)receive:(SEL)aSelector andReturn:(id)aValue withCountAtLeast:(NSUInteger)aCount {
137 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
138 [self receiveMessagePattern:messagePattern andReturn:aValue countType:KWCountTypeAtLeast count:aCount];
139}
140
141- (void)receive:(SEL)aSelector andReturn:(id)aValue withCountAtMost:(NSUInteger)aCount {
142 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
143 [self receiveMessagePattern:messagePattern andReturn:aValue countType:KWCountTypeAtMost count:aCount];
144}
145
146- (void)receiveMessagePattern:(KWMessagePattern *)aMessagePattern countType:(KWCountType)aCountType count:(NSUInteger)aCount {
147#if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
148 @try {
149#endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
150
151 [self.subject stubMessagePattern:aMessagePattern andReturn:nil overrideExisting:NO];
152 self.messageTracker = [KWMessageTracker messageTrackerWithSubject:self.subject messagePattern:aMessagePattern countType:aCountType count:aCount];
153
154#if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
155 } @catch(NSException *exception) {
156 KWSetExceptionFromAcrossInvocationBoundary(exception);
157 }
158#endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
159}
160
161- (void)receiveMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue countType:(KWCountType)aCountType count:(NSUInteger)aCount {
162#if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
163 @try {
164#endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
165
166 [self.subject stubMessagePattern:aMessagePattern andReturn:aValue];
167 self.messageTracker = [KWMessageTracker messageTrackerWithSubject:self.subject messagePattern:aMessagePattern countType:aCountType count:aCount];
168
169#if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
170 } @catch(NSException *exception) {
171 KWSetExceptionFromAcrossInvocationBoundary(exception);
172 }
173#endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
174}
175
176#pragma mark -
177#pragma mark Capturing Invocations
178
179+ (NSMethodSignature *)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer methodSignatureForSelector:(SEL)aSelector {
180 KWMatchVerifier *verifier = (anInvocationCapturer.userInfo)[MatchVerifierKey];
181
182 if ([verifier.subject respondsToSelector:aSelector])
183 return [verifier.subject methodSignatureForSelector:aSelector];
184
185 NSString *encoding = KWEncodingForVoidMethod();
186 return [NSMethodSignature signatureWithObjCTypes:[encoding UTF8String]];
187}
188
189+ (void)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer didCaptureInvocation:(NSInvocation *)anInvocation {
190 NSDictionary *userInfo = anInvocationCapturer.userInfo;
191 id verifier = userInfo[MatchVerifierKey];
192 KWCountType countType = [userInfo[CountTypeKey] unsignedIntegerValue];
193 NSUInteger count = [userInfo[CountKey] unsignedIntegerValue];
194 NSValue *stubValue = userInfo[StubValueKey];
195 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternFromInvocation:anInvocation];
196
197 if (stubValue != nil)
198 [verifier receiveMessagePattern:messagePattern andReturn:[stubValue nonretainedObjectValue] countType:countType count:count];
199 else
200 [verifier receiveMessagePattern:messagePattern countType:countType count:count];
201}
202
203@end
204
205@implementation KWMatchVerifier(KWReceiveMatcherAdditions)
206
207#pragma mark -
208#pragma mark Verifying
209
210- (void)receive:(SEL)aSelector withArguments:(id)firstArgument, ... {
211 va_list argumentList;
212 va_start(argumentList, firstArgument);
213 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
214 [(id)self receiveMessagePattern:messagePattern countType:KWCountTypeExact count:1];
215}
216
217- (void)receive:(SEL)aSelector withCount:(NSUInteger)aCount arguments:(id)firstArgument, ... {
218 va_list argumentList;
219 va_start(argumentList, firstArgument);
220 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
221 [(id)self receiveMessagePattern:messagePattern countType:KWCountTypeExact count:aCount];
222}
223
224- (void)receive:(SEL)aSelector withCountAtLeast:(NSUInteger)aCount arguments:(id)firstArgument, ... {
225 va_list argumentList;
226 va_start(argumentList, firstArgument);
227 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
228 [(id)self receiveMessagePattern:messagePattern countType:KWCountTypeAtLeast count:aCount];
229}
230
231- (void)receive:(SEL)aSelector withCountAtMost:(NSUInteger)aCount arguments:(id)firstArgument, ... {
232 va_list argumentList;
233 va_start(argumentList, firstArgument);
234 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
235 [(id)self receiveMessagePattern:messagePattern countType:KWCountTypeAtMost count:aCount];
236}
237
238- (void)receive:(SEL)aSelector andReturn:(id)aValue withArguments:(id)firstArgument, ... {
239 va_list argumentList;
240 va_start(argumentList, firstArgument);
241 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
242 [(id)self receiveMessagePattern:messagePattern andReturn:aValue countType:KWCountTypeExact count:1];
243}
244
245- (void)receive:(SEL)aSelector andReturn:(id)aValue withCount:(NSUInteger)aCount arguments:(id)firstArgument, ... {
246 va_list argumentList;
247 va_start(argumentList, firstArgument);
248 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
249 [(id)self receiveMessagePattern:messagePattern andReturn:aValue countType:KWCountTypeExact count:aCount];
250}
251
252- (void)receive:(SEL)aSelector andReturn:(id)aValue withCountAtLeast:(NSUInteger)aCount arguments:(id)firstArgument, ... {
253 va_list argumentList;
254 va_start(argumentList, firstArgument);
255 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
256 [(id)self receiveMessagePattern:messagePattern andReturn:aValue countType:KWCountTypeAtLeast count:aCount];
257}
258
259- (void)receive:(SEL)aSelector andReturn:(id)aValue withCountAtMost:(NSUInteger)aCount arguments:(id)firstArgument, ... {
260 va_list argumentList;
261 va_start(argumentList, firstArgument);
262 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
263 [(id)self receiveMessagePattern:messagePattern andReturn:aValue countType:KWCountTypeAtMost count:aCount];
264}
265
266#pragma mark Invocation Capturing Methods
267
268- (NSDictionary *)userInfoForReceiveMatcherWithCountType:(KWCountType)aCountType count:(NSUInteger)aCount {
269 return @{MatchVerifierKey: self,
270 CountTypeKey: @(aCountType),
271 CountKey: @(aCount)};
272}
273
274- (NSDictionary *)userInfoForReceiveMatcherWithCountType:(KWCountType)aCountType count:(NSUInteger)aCount value:(id)aValue {
275 return @{MatchVerifierKey: self,
276 CountTypeKey: @(aCountType),
277 CountKey: @(aCount),
278 StubValueKey: [NSValue valueWithNonretainedObject:aValue]};
279}
280
281- (id)receive {
282 NSDictionary *userInfo = [self userInfoForReceiveMatcherWithCountType:KWCountTypeExact count:1];
283 return [KWInvocationCapturer invocationCapturerWithDelegate:[KWReceiveMatcher class] userInfo:userInfo];
284}
285
286- (id)receiveWithCount:(NSUInteger)aCount {
287 NSDictionary *userInfo = [self userInfoForReceiveMatcherWithCountType:KWCountTypeExact count:aCount];
288 return [KWInvocationCapturer invocationCapturerWithDelegate:[KWReceiveMatcher class] userInfo:userInfo];
289}
290
291- (id)receiveWithCountAtLeast:(NSUInteger)aCount {
292 NSDictionary *userInfo = [self userInfoForReceiveMatcherWithCountType:KWCountTypeAtLeast count:aCount];
293 return [KWInvocationCapturer invocationCapturerWithDelegate:[KWReceiveMatcher class] userInfo:userInfo];
294}
295
296- (id)receiveWithCountAtMost:(NSUInteger)aCount {
297 NSDictionary *userInfo = [self userInfoForReceiveMatcherWithCountType:KWCountTypeAtMost count:aCount];
298 return [KWInvocationCapturer invocationCapturerWithDelegate:[KWReceiveMatcher class] userInfo:userInfo];
299}
300
301- (id)receiveAndReturn:(id)aValue {
302 NSDictionary *userInfo = [self userInfoForReceiveMatcherWithCountType:KWCountTypeExact count:1 value:aValue];
303 return [KWInvocationCapturer invocationCapturerWithDelegate:[KWReceiveMatcher class] userInfo:userInfo];
304}
305
306- (id)receiveAndReturn:(id)aValue withCount:(NSUInteger)aCount {
307 NSDictionary *userInfo = [self userInfoForReceiveMatcherWithCountType:KWCountTypeExact count:aCount value:aValue];
308 return [KWInvocationCapturer invocationCapturerWithDelegate:[KWReceiveMatcher class] userInfo:userInfo];
309}
310
311- (id)receiveAndReturn:(id)aValue withCountAtLeast:(NSUInteger)aCount {
312 NSDictionary *userInfo = [self userInfoForReceiveMatcherWithCountType:KWCountTypeAtLeast count:aCount value:aValue];
313 return [KWInvocationCapturer invocationCapturerWithDelegate:[KWReceiveMatcher class] userInfo:userInfo];
314}
315
316- (id)receiveAndReturn:(id)aValue withCountAtMost:(NSUInteger)aCount {
317 NSDictionary *userInfo = [self userInfoForReceiveMatcherWithCountType:KWCountTypeAtMost count:aCount value:aValue];
318 return [KWInvocationCapturer invocationCapturerWithDelegate:[KWReceiveMatcher class] userInfo:userInfo];
319}
320
321@end