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