]> git.wincent.com - WOTest.git/blob - WOMock.m
Fix object-to-pointer comparisons on Leopard
[WOTest.git] / WOMock.m
1 //
2 //  WOMock.m
3 //  WOTest
4 //
5 //  Created by Wincent Colaiuta on 14 June 2005.
6 //
7 //  Copyright 2005-2007 Wincent Colaiuta.
8 //  This program is free software: you can redistribute it and/or modify
9 //  it under the terms of the GNU General Public License as published by
10 //  the Free Software Foundation, either version 3 of the License, or
11 //  (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 //
21
22 // class header
23 #import "WOMock.h"
24
25 // system headers
26 #import <objc/objc-class.h>
27
28 // framework headers
29 #import "NSInvocation+WOTest.h"
30 #import "NSMethodSignature+WOTest.h"
31 #import "NSObject+WOTest.h"
32 #import "NSProxy+WOTest.h"
33 #import "NSValue+WOTest.h"
34 #import "WOClassMock.h"
35 #import "WOEnumerate.h"
36 #import "WOObjectMock.h"
37 #import "WOProtocolMock.h"
38
39 @implementation WOMock
40
41 #pragma mark -
42 #pragma mark Creation
43
44 + (id)mockForObjectClass:(Class)aClass
45 {
46     // avoid infinite loops if called by subclass
47     if (self != [WOMock class])
48         [NSException raise:NSInternalInconsistencyException format:@"mockForObjectClass: called from WOMock subclass"]; 
49     
50     return [WOObjectMock mockForClass:aClass];
51 }
52
53 + (id)mockForClass:(Class)aClass
54 {
55     // avoid infinite loops if called by subclass
56     if (self != [WOMock class])
57         [NSException raise:NSInternalInconsistencyException format:@"mockForClass: called from WOMock subclass"]; 
58
59     return [WOClassMock mockForClass:aClass];
60 }
61
62 + (id)mockForProtocol:(Protocol *)aProtocol
63 {
64     // avoid infinite loops if called by subclass
65     if (self != [WOMock class])
66         [NSException raise:NSInternalInconsistencyException format:@"mockForProtocol: called from WOMock subclass"]; 
67
68     return [WOProtocolMock mockForProtocol:aProtocol];
69 }
70
71 // a true Apple-style cluster would do this by allocating a placeholder object
72 - (id)initWithObjectClass:(Class)aClass
73 {
74     // avoid infinite loops if called by subclass
75     if ([self class] != [WOMock class]) 
76         [NSException raise:NSInternalInconsistencyException format:@"initWithObjectClass: called from WOMock subclass"];
77     
78     id subclass = [[WOObjectMock allocWithZone:[self zone]] initWithClass:aClass];
79
80     [self dealloc];
81     return subclass;
82 }
83
84 // a true Apple-style cluster would do this by allocating a placeholder object
85 - (id)initWithClass:(Class)aClass
86 {
87     // avoid infinite loops if called by subclass
88     if ([self class] != [WOMock class])
89         [NSException raise:NSInternalInconsistencyException format:@"initWithClass: called from WOMock subclass"]; 
90     
91     id subclass = [[WOClassMock allocWithZone:[self zone]] initWithClass:aClass]; 
92     [self dealloc];
93     return subclass;    
94 }
95
96 // a true Apple-style cluster would do this by allocating a placeholder object
97 - (id)initWithProtocol:(Protocol *)aProtocol
98 {
99     // avoid infinite loops if called by subclass
100     if ([self class] != [WOMock class])
101         [NSException raise:NSInternalInconsistencyException format:@"initWithProtocol: called from WOMock subclass"]; 
102     
103     id subclass = [[WOProtocolMock allocWithZone:[self zone]] initWithProtocol:aProtocol]; 
104     [self dealloc];
105     return subclass;    
106 }
107
108 - (id)init
109 {
110     // super (NSProxy) has no init method
111     expectedInOrder     = [[NSMutableArray alloc] init];    // there are no accessors (to avoid namespace pollution)
112     accepted            = [[NSMutableSet alloc] init];
113     acceptedOnce        = [[NSMutableSet alloc] init];
114     expected            = [[NSMutableSet alloc] init];
115     expectedOnce        = [[NSMutableSet alloc] init];
116     rejected            = [[NSMutableSet alloc] init];
117     methodSignatures    = [[NSMutableDictionary alloc] init];
118     return self;
119 }
120
121 - (void)dealloc
122 {
123     [self               verify];
124     [accepted           release];
125     [acceptedOnce       release];
126     [expected           release];
127     [expectedOnce       release];
128     [expectedInOrder    release];
129     [rejected           release];
130     [methodSignatures   release];
131     [super dealloc];
132 }
133
134 - (id)accept
135 {
136     [NSException raise:NSInternalInconsistencyException format:@"accept invoked on WOMock abstract class"]; 
137     return nil;
138 }
139
140 - (id)acceptOnce
141 {
142     [NSException raise:NSInternalInconsistencyException format:@"acceptOnce invoked on WOMock abstract class"]; 
143     return nil;
144 }
145
146 - (id)reject
147 {
148     [NSException raise:NSInternalInconsistencyException format:@"reject invoked on WOMock abstract class"]; 
149     return nil;
150 }
151
152 - (id)expect
153 {
154     [NSException raise:NSInternalInconsistencyException format:@"expect invoked on WOMock abstract class"]; 
155     return nil;
156 }
157
158 - (id)expectOnce
159 {
160     [NSException raise:NSInternalInconsistencyException format:@"expectOnce invoked on WOMock abstract class"]; 
161     return nil;
162 }
163
164 - (id)expectInOrder
165 {
166     [NSException raise:NSInternalInconsistencyException format:@"expectInOrder invoked on WOMock abstract class"]; 
167     return nil;
168 }
169
170 - (void)clear
171 {
172     [accepted           removeAllObjects];
173     [acceptedOnce       removeAllObjects];
174     [expected           removeAllObjects];
175     [expectedOnce       removeAllObjects];
176     [expectedInOrder    removeAllObjects];
177     [rejected           removeAllObjects];
178 }
179
180 - (void)verify
181 {
182     NSAssert(([expected count] == 0),           @"verification failure ('expected' set not empty)");
183     NSAssert(([expectedOnce count] == 0),       @"verification failure ('expectedOnce' set not empty)");
184     NSAssert(([expectedInOrder count] == 0),    @"verification failure ('expectedInOrder' set not empty)");
185 }
186
187 #pragma mark -
188 #pragma mark Utility methods
189
190 - (void)storeReturnValue:(NSValue *)aValue forInvocation:(NSInvocation *)invocation 
191 {
192     NSParameterAssert(invocation != nil);
193     if (!aValue) return; // nothing to do
194     const char *returnType = [[invocation methodSignature] methodReturnType];
195     const char *storedType = [aValue objCType];
196     NSAssert2((strcmp(returnType, storedType) == 0),
197              @"Cannot store mismatched return type in invocation (%s, %s)", returnType, storedType);
198
199     size_t bufferSize = [aValue WOTest_bufferSize];
200     void *buffer = malloc(bufferSize);
201     NSAssert1((buffer != NULL), @"malloc() failed (size %d)", bufferSize); 
202     [aValue getValue:buffer];
203     [invocation setReturnValue:buffer];
204 }
205
206 - (void)setObjCTypes:(NSString *)types forSelector:(SEL)aSelector
207 {
208     [methodSignatures setObject:[NSMethodSignature WOTest_signatureBasedOnObjCTypes:[types UTF8String]] 
209                          forKey:NSStringFromSelector(aSelector)];
210 }
211
212 #pragma mark -
213 #pragma mark NSProxy (private)
214
215 - forward:(SEL)sel :(marg_list)args
216 {
217     // let standard event flow take place (but note that NSProxy implementation raises so subclasses must do the real work)
218     if ([self methodSignatureForSelector:sel])
219         return [super forward:sel :args];
220
221     // fallback to internal lookup
222     NSMethodSignature *signature = [methodSignatures objectForKey:NSStringFromSelector(sel)];
223         
224     // at this point it would be great to be able to improvise but it's not possible to figure out an accurate method signature
225     NSAssert((signature != nil), ([NSString stringWithFormat:@"no method signature for selector %@", NSStringFromSelector(sel)]));
226     
227     NSInvocation *forwardInvocation = [NSInvocation invocationWithMethodSignature:signature];
228     [forwardInvocation setSelector:sel];
229         
230     // store arguments in invocation
231     int offset = 0;
232     for (unsigned i = 0, max = [signature numberOfArguments]; i < max; i++)
233     {
234         const char *type = [signature getArgumentTypeAtIndex:i];    // always id, SEL, ...
235         
236 #if defined(__ppc__)
237
238         // TODO: finish version in WOStub and copy it here
239         // leave the compiler warnings about "unused variable 'type'" and "unused variable 'offset'" to remind me to do it
240         // may be able to use libffi to help here    
241         
242 #elif defined(__i386__)
243             
244         // on i386 the marg_getRef macro and its helper, marg_adjustedOffset, should work fine
245         if (strcmp(type, @encode(id)) == 0)
246         {
247             id *ref = marg_getRef(args, offset, id);
248             [forwardInvocation WOTest_setArgumentValue:[NSValue valueWithBytes:ref objCType:type] atIndex:i];
249         }
250         else if (strcmp(type, @encode(SEL)) == 0)
251         {
252             SEL *ref = marg_getRef(args, offset, SEL);
253             [forwardInvocation WOTest_setArgumentValue:[NSValue valueWithBytes:ref objCType:type] atIndex:i];
254         }
255         else
256             [NSException raise:NSGenericException format:@"type %s not supported", type];     
257                 
258         offset += [NSValue WOTest_sizeForType:[NSString stringWithUTF8String:type]];
259         
260 #elif defined(__ppc64__)
261         // there is no objc-msg-ppc.s so for now just omit support rather than make assumptions
262 #error ppc64 not supported yet
263         
264 #else
265         
266 #error Unsupported architecture
267         
268 #endif
269         
270     }
271     [self forwardInvocation:forwardInvocation]; // stores return value in invocation
272     
273     unsigned bufferSize = [signature methodReturnLength];
274     void *returnBuffer = malloc(bufferSize);
275     NSAssert1((returnBuffer != NULL), @"malloc() failed (size %d)", bufferSize);
276     [forwardInvocation getReturnValue:&returnBuffer];
277     return returnBuffer; // TODO: cast according to the return type
278 }
279     
280 #pragma mark -
281 #pragma mark Accessors
282
283 - (BOOL)acceptsByDefault
284 {
285     return acceptsByDefault;
286 }
287
288 - (void)setAcceptsByDefault:(BOOL)flag
289 {
290     acceptsByDefault = flag;
291 }
292
293 - (NSMutableDictionary *)methodSignatures
294 {
295     return methodSignatures;
296 }
297
298 @end