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