main
  1//
  2// Licensed under the terms in License.txt
  3//
  4// Copyright 2010 Allen Ding. All rights reserved.
  5//
  6
  7#import "NSObject+KiwiStubAdditions.h"
  8#import "KWIntercept.h"
  9#import "KWInvocationCapturer.h"
 10#import "KWMessagePattern.h"
 11#import "KWObjCUtilities.h"
 12#import "KWStringUtilities.h"
 13#import "KWStub.h"
 14
 15static NSString * const StubValueKey = @"StubValueKey";
 16static NSString * const StubSecondValueKey = @"StubSecondValueKey";
 17static NSString * const ChangeStubValueAfterTimesKey = @"ChangeStubValueAfterTimesKey";
 18
 19@implementation NSObject(KiwiStubAdditions)
 20
 21#pragma mark -
 22#pragma mark Capturing Invocations
 23
 24- (NSMethodSignature *)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer methodSignatureForSelector:(SEL)aSelector {
 25    NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
 26
 27    if (signature != nil)
 28        return signature;
 29
 30    NSString *encoding = KWEncodingForVoidMethod();
 31    return [NSMethodSignature signatureWithObjCTypes:[encoding UTF8String]];
 32}
 33
 34- (void)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer didCaptureInvocation:(NSInvocation *)anInvocation {
 35    KWMessagePattern *messagePattern = [KWMessagePattern messagePatternFromInvocation:anInvocation];
 36    id value = (anInvocationCapturer.userInfo)[StubValueKey];
 37    if (!(anInvocationCapturer.userInfo)[StubSecondValueKey]) {
 38        [self stubMessagePattern:messagePattern andReturn:value];
 39    } else {
 40        id times = (anInvocationCapturer.userInfo)[ChangeStubValueAfterTimesKey];
 41        id secondValue = (anInvocationCapturer.userInfo)[StubSecondValueKey];
 42        [self stubMessagePattern:messagePattern andReturn:value times:times afterThatReturn:secondValue];
 43    }
 44}
 45
 46#pragma mark -
 47#pragma mark Stubbing Methods
 48
 49- (void)stub:(SEL)aSelector {
 50    KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
 51    [self stubMessagePattern:messagePattern andReturn:nil];
 52}
 53
 54- (void)stub:(SEL)aSelector withBlock:(id (^)(NSArray *))block {
 55    KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
 56    [self stubMessagePattern:messagePattern withBlock:block];
 57}
 58
 59- (void)stub:(SEL)aSelector withArguments:(id)firstArgument, ... {
 60    va_list argumentList;
 61    va_start(argumentList, firstArgument);
 62    KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
 63    [self stubMessagePattern:messagePattern andReturn:nil];
 64}
 65
 66- (void)stub:(SEL)aSelector andReturn:(id)aValue {
 67    KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
 68    [self stubMessagePattern:messagePattern andReturn:aValue];
 69}
 70
 71- (void)stub:(SEL)aSelector andReturn:(id)aValue withArguments:(id)firstArgument, ... {
 72    va_list argumentList;
 73    va_start(argumentList, firstArgument);
 74    KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
 75    [self stubMessagePattern:messagePattern andReturn:aValue];
 76}
 77
 78+ (void)stub:(SEL)aSelector {
 79    KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
 80    [self stubMessagePattern:messagePattern andReturn:nil];
 81}
 82
 83+ (void)stub:(SEL)aSelector withBlock:(id (^)(NSArray *))block {
 84    KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
 85    [self stubMessagePattern:messagePattern withBlock:block];
 86}
 87
 88+ (void)stub:(SEL)aSelector withArguments:(id)firstArgument, ... {
 89    va_list argumentList;
 90    va_start(argumentList, firstArgument);
 91    KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
 92    [self stubMessagePattern:messagePattern andReturn:nil];
 93}
 94
 95+ (void)stub:(SEL)aSelector andReturn:(id)aValue {
 96    KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
 97    [self stubMessagePattern:messagePattern andReturn:aValue];
 98}
 99
100+ (void)stub:(SEL)aSelector andReturn:(id)aValue withArguments:(id)firstArgument, ... {
101    va_list argumentList;
102    va_start(argumentList, firstArgument);
103    KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
104    [self stubMessagePattern:messagePattern andReturn:aValue];
105}
106
107- (id)stub {
108    return [KWInvocationCapturer invocationCapturerWithDelegate:self];
109}
110
111- (id)stubAndReturn:(id)aValue {
112    NSDictionary *userInfo = @{StubValueKey: aValue};
113    return [KWInvocationCapturer invocationCapturerWithDelegate:self userInfo:userInfo];
114}
115
116- (id)stubAndReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue {
117    NSDictionary *userInfo = @{StubValueKey: aValue, ChangeStubValueAfterTimesKey: times, StubSecondValueKey: aSecondValue};
118    return [KWInvocationCapturer invocationCapturerWithDelegate:self userInfo:userInfo];
119}
120
121- (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue {
122    [self stubMessagePattern:aMessagePattern andReturn:aValue overrideExisting:YES];
123}
124
125- (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue overrideExisting:(BOOL)overrideExisting {
126    if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
127        [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists",
128         NSStringFromSelector(aMessagePattern.selector)];
129    }
130    
131    Class interceptClass = KWSetupObjectInterceptSupport(self);
132    KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
133    KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern value:aValue];
134    KWAssociateObjectStub(self, stub, overrideExisting);
135}
136
137- (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue {
138    if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
139        [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists",
140         NSStringFromSelector(aMessagePattern.selector)];
141    }
142
143    Class interceptClass = KWSetupObjectInterceptSupport(self);
144    KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
145    KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern value:aValue times:times afterThatReturn:aSecondValue];
146    KWAssociateObjectStub(self, stub, YES);
147}
148
149- (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block {
150    if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
151        [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists",
152         NSStringFromSelector(aMessagePattern.selector)];
153    }
154    
155    Class interceptClass = KWSetupObjectInterceptSupport(self);
156    KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
157    KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern block:block];
158    KWAssociateObjectStub(self, stub, YES);
159}
160
161+ (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue
162{
163    [self stubMessagePattern:aMessagePattern andReturn:aValue overrideExisting:YES];
164}
165
166+ (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue overrideExisting:(BOOL)override
167{
168    if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
169        [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists",
170         NSStringFromSelector(aMessagePattern.selector)];
171    }
172    
173    Class interceptClass = KWSetupObjectInterceptSupport(self);
174    KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
175    KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern value:aValue];
176    KWAssociateObjectStub(self, stub, override);
177}
178
179+ (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue {
180    [self stubMessagePattern:aMessagePattern andReturn:aValue times:times afterThatReturn:aSecondValue overrideExisting:YES];
181}
182
183+ (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue overrideExisting:(BOOL)override {
184    if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
185        [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists",
186         NSStringFromSelector(aMessagePattern.selector)];
187    }
188    
189    Class interceptClass = KWSetupObjectInterceptSupport(self);
190    KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
191    KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern value:aValue times:times afterThatReturn:aSecondValue];
192    KWAssociateObjectStub(self, stub, override);
193}
194
195+ (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block {
196    [self stubMessagePattern:aMessagePattern withBlock:block overrideExisting:YES];
197}
198
199+ (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block  overrideExisting:(BOOL)override {
200    if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
201        [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists",
202         NSStringFromSelector(aMessagePattern.selector)];
203    }
204    
205    Class interceptClass = KWSetupObjectInterceptSupport(self);
206    KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
207    KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern block:block];
208    KWAssociateObjectStub(self, stub, override);
209}
210
211- (void)clearStubs {
212    KWClearObjectStubs(self);
213}
214
215#pragma mark -
216#pragma mark Spying on Messages
217
218- (void)addMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
219    if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
220        [NSException raise:@"KWSpyException" format:@"cannot add spy for -%@ because no such method exists",
221         NSStringFromSelector(aMessagePattern.selector)];
222    }
223
224    Class interceptClass = KWSetupObjectInterceptSupport(self);
225    KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
226    KWAssociateMessageSpy(self, aSpy, aMessagePattern);
227}
228
229- (void)removeMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
230    KWClearObjectSpy(self, aSpy, aMessagePattern);
231}
232
233+ (void)addMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
234    if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
235        [NSException raise:@"KWSpyException" format:@"cannot add spy for -%@ because no such method exists",
236         NSStringFromSelector(aMessagePattern.selector)];
237    }
238    
239    Class interceptClass = KWSetupObjectInterceptSupport(self);
240    KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
241    KWAssociateMessageSpy(self, aSpy, aMessagePattern);
242}
243
244+ (void)removeMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
245    KWClearObjectSpy(self, aSpy, aMessagePattern);
246}
247
248@end