]> git.wincent.com - WOTest.git/blob - WOObjectMock.m
Fix object-to-pointer comparisons on Leopard
[WOTest.git] / WOObjectMock.m
1 //
2 //  WOObjectMock.m
3 //  WOTest
4 //
5 //  Created by Wincent Colaiuta on 10 February 2006.
6 //
7 //  Copyright 2006-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 "WOObjectMock.h"
24
25 // framework headers
26 #import "NSInvocation+WOTest.h"
27 #import "NSObject+WOTest.h"
28 #import "WOEnumerate.h"
29 #import "WOObjectStub.h"
30
31 @implementation WOObjectMock
32
33 #pragma mark -
34 #pragma mark Creation
35
36 + (id)mockForClass:(Class)aClass
37 {
38     NSParameterAssert(aClass != NULL);
39     NSParameterAssert([NSObject WOTest_isRegisteredClass:aClass]);     // only registered classes pass (do not pass meta classes)
40     return [[[self alloc] initWithClass:aClass] autorelease];
41 }
42
43 - (id)initWithClass:(Class)aClass
44 {
45     NSParameterAssert(aClass != NULL);
46     NSParameterAssert([NSObject WOTest_isRegisteredClass:aClass]);     // only registered classes pass (do not pass meta classes)
47
48     if ((self = [super init]))
49         [self setMockedClass:aClass];                
50     return self;
51 }
52
53 #pragma mark -
54 #pragma mark Recording
55
56 - (id)accept
57 {
58     WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
59     [accepted addObject:stub];
60     return stub;
61 }
62
63 - (id)acceptOnce
64 {
65     WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
66     [acceptedOnce addObject:stub];
67     return stub;
68 }
69
70 - (id)reject
71 {
72     WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
73     [rejected addObject:stub];
74     return stub;
75 }
76
77 - (id)expect
78 {
79     WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
80     [expected addObject:stub];
81     return stub;
82 }
83
84 - (id)expectOnce
85 {
86     WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
87     [expectedOnce addObject:stub];
88     return stub;
89 }
90
91 - (id)expectInOrder
92 {
93     WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
94     [expectedInOrder addObject:stub];
95     return stub;
96 }
97
98 #pragma mark -
99 #pragma mark Proxy methods 
100
101 - (void)forwardInvocation:(NSInvocation *)anInvocation
102 {
103     NSParameterAssert(anInvocation != nil);
104     
105     // check if in reject list
106     WO_ENUMERATE(rejected, stub)
107         if ([stub matchesInvocation:anInvocation])
108         {
109             [NSException raise:NSInternalInconsistencyException format:@"Rejected selector %@ for class %@",
110                 NSStringFromSelector([anInvocation selector]), NSStringFromClass([self mockedClass])];
111             return;
112         }
113     
114     // check if expectedInOrder
115     WO_ENUMERATE(expectedInOrder, stub)
116         if ([stub matchesInvocation:anInvocation])
117         {
118             NSAssert2(([expectedInOrder objectAtIndex:0] == stub), @"Invocation selector %@ class %@ received out of order",
119                       NSStringFromSelector([anInvocation selector]), NSStringFromClass([self mockedClass]));
120             
121             [expectedInOrder removeObjectAtIndex:0];    // if in order, remove from head of list
122             
123             // and move to "accepted"
124             [accepted addObject:stub];
125             [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
126             if ([stub exception]) @throw [stub exception];
127             return;
128         }
129
130     // check if expected once
131     WO_ENUMERATE(expectedOnce, stub)
132         if ([stub matchesInvocation:anInvocation])
133         {
134             // move object from "expectedOnce" to "rejected"
135             [rejected addObject:stub];
136             [expectedOnce removeObject:stub];
137             [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
138             if ([stub exception]) @throw [stub exception];
139             return;
140         }
141     
142     // check if expected
143     WO_ENUMERATE(expected, stub)
144         if ([stub matchesInvocation:anInvocation])
145         {
146             // move from "expected" to "accepted"
147             [accepted addObject:stub];
148             [expected removeObject:stub];
149             [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
150             if ([stub exception]) @throw [stub exception];
151             return;
152         }
153     
154     // check if accepted once
155     WO_ENUMERATE(acceptedOnce, stub)
156         if ([stub matchesInvocation:anInvocation])
157         {
158             // move from "acceptedOnce" to "rejected"
159             [rejected addObject:stub];
160             [acceptedOnce removeObject:stub];
161             [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
162             if ([stub exception]) @throw [stub exception];
163             return;
164         }
165     
166     // check if accepted
167     WO_ENUMERATE(accepted, stub)
168         if ([stub matchesInvocation:anInvocation])
169         {
170             [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
171             if ([stub exception]) @throw [stub exception];
172             return;
173         }
174     
175     if ([self acceptsByDefault]) return;
176     
177     // no matches! (should never get here)
178     [NSException raise:NSInternalInconsistencyException format:@"No matching invocations found (selector %@, class %@)",
179         NSStringFromSelector([anInvocation selector]), NSStringFromClass([self mockedClass])];
180 }
181
182 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
183 {
184     // avoid an infinite loop (docs warn "Be sure to avoid an infinite loop when necessary by checking that aSelector isn't the 
185     // selector for this method itself and by not sending any message that might invoke this method.")
186     if (([self mockedClass] == [self class]) && (aSelector == _cmd)) return nil;
187     
188     // search only for instance methods here; forcing the programmer to use WOClassMock for searching for class methods avoids 
189     // ambiguity in cases where an instance method and a class method share the same name
190     return [[self mockedClass] instanceMethodSignatureForSelector:aSelector];
191 }
192
193 #pragma mark -
194 #pragma mark Accessors
195
196 - (Class)mockedClass
197 {
198     return mockedClass;
199 }
200
201 - (void)setMockedClass:(Class)aMockedClass
202 {
203     mockedClass = aMockedClass;
204 }
205
206 @end