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