]> git.wincent.com - WOTest.git/blob - WOProtocolMock.m
Code clean-up for garbage collection
[WOTest.git] / WOProtocolMock.m
1 //
2 //  WOProtocolMock.m
3 //  WOTest
4 //
5 //  Created by Wincent Colaiuta on 28 January 2007.
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 #import "WOProtocolMock.h"
23
24 // system headers
25
26 #import <objc/Protocol.h>
27
28 // framework headers
29
30 #import "NSInvocation+WOTest.h"
31 #import "NSMethodSignature+WOTest.h"
32 #import "WOProtocolStub.h"
33
34 #pragma mark -
35 #pragma mark C function implementations
36
37 NSString *WOStringFromProtocol(Protocol *aProtocol)
38 {
39     if (aProtocol == NULL) return nil;
40     return [NSString stringWithUTF8String:protocol_getName(aProtocol)];
41 }
42
43 @implementation WOProtocolMock
44
45 #pragma mark -
46 #pragma mark Creation
47
48 + (id)mockForProtocol:(Protocol *)aProtocol
49 {
50     NSParameterAssert(aProtocol != NULL);
51     return [[self alloc] initWithProtocol:aProtocol];
52 }
53
54 - (id)initWithProtocol:(Protocol *)aProtocol
55 {
56     NSParameterAssert(aProtocol != NULL);
57
58     // TODO: test for validity of the Protocol by checking with the runtime
59
60     if ((self = [super init]))
61         [self setMockedProtocol:aProtocol];
62     return self;
63 }
64
65 #pragma mark -
66 #pragma mark Recording
67
68 - (id)accept
69 {
70     WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
71     [accepted addObject:stub];
72     return stub;
73 }
74
75 - (id)acceptOnce
76 {
77     WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
78     [acceptedOnce addObject:stub];
79     return stub;
80 }
81
82 - (id)reject
83 {
84     WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
85     [rejected addObject:stub];
86     return stub;
87 }
88
89 - (id)expect
90 {
91     WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
92     [expected addObject:stub];
93     return stub;
94 }
95
96 - (id)expectOnce
97 {
98     WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
99     [expectedOnce addObject:stub];
100     return stub;
101 }
102
103 - (id)expectInOrder
104 {
105     WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
106     [expectedInOrder addObject:stub];
107     return stub;
108 }
109
110 #pragma mark -
111 #pragma mark Proxy methods
112
113 - (void)forwardInvocation:(NSInvocation *)anInvocation
114 {
115     NSParameterAssert(anInvocation != nil);
116
117     // check if in reject list
118     for (WOStub *stub in rejected)
119         if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
120         {
121             [NSException raise:NSInternalInconsistencyException format:@"Rejected selector %@ for protocol %@",
122                 NSStringFromSelector([anInvocation selector]), WOStringFromProtocol([self mockedProtocol])];
123             return;
124         }
125
126     // check if expectedInOrder
127     for (WOStub *stub in expectedInOrder)
128         if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
129         {
130             NSAssert(([expectedInOrder objectAtIndex:0] != stub), @"invocation received out of order");
131
132             // if in order, remove from head of list
133             [expectedInOrder removeObjectAtIndex:0];
134
135             // and move to "accepted"
136             [accepted addObject:stub];
137             [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
138             if ([stub exception]) @throw [stub exception];
139             return;
140         }
141
142     // check if expected once
143     for (WOStub *stub in expectedOnce)
144         if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
145         {
146             // move object from "expectedOnce" to "rejected"
147             [rejected addObject:stub];
148             [expectedOnce removeObject:stub];
149             [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
150             if ([stub exception]) @throw [stub exception];
151             return;
152         }
153
154     // check if expected
155     for (WOStub *stub in expected)
156         if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
157         {
158             // move from "expected" to "accepted"
159             [accepted addObject:stub];
160             [expected removeObject:stub];
161             [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
162             if ([stub exception]) @throw [stub exception];
163             return;
164         }
165
166     // check if accepted once
167     for (WOStub *stub in acceptedOnce)
168         if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
169         {
170             // move from "acceptedOnce" to "rejected"
171             [rejected addObject:stub];
172             [acceptedOnce removeObject:stub];
173             [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
174             if ([stub exception]) @throw [stub exception];
175             return;
176         }
177
178     // check if accepted
179     for (WOStub *stub in accepted)
180         if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
181         {
182             [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
183             if ([stub exception]) @throw [stub exception];
184             return;
185         }
186
187
188     if ([self acceptsByDefault]) return;
189
190     // no matches! (should never get here)
191     [NSException raise:NSInternalInconsistencyException format:@"No matching invocations found (selector %@, protocol %@)",
192         NSStringFromSelector([anInvocation selector]), WOStringFromProtocol([self mockedProtocol])];
193 }
194
195 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
196 {
197     // avoid an infinite loop (docs warn "Be sure to avoid an infinite loop when necessary by checking that aSelector isn't the
198     // selector for this method itself and by not sending any message that might invoke this method.")
199     if (aSelector == _cmd) return nil;
200
201     BOOL isRequiredMethod = YES;    // no idea what to pass here
202     struct objc_method_description description = protocol_getMethodDescription([self mockedProtocol], aSelector, isRequiredMethod, YES);
203     return [NSMethodSignature WOTest_signatureBasedOnObjCTypes:description.types];
204 }
205
206 #pragma mark -
207 #pragma mark Accessors
208
209 - (Protocol *)mockedProtocol
210 {
211     return mockedProtocol;
212 }
213
214 - (void)setMockedProtocol:(Protocol *)aMockedProtocol
215 {
216     mockedProtocol = aMockedProtocol;
217 }
218
219 @end