5 // Created by Wincent Colaiuta on 28 January 2007.
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.
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.
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/>.
22 #import "WOProtocolMock.h"
26 #import <objc/Protocol.h>
30 #import "NSInvocation+WOTest.h"
31 #import "NSMethodSignature+WOTest.h"
32 #import "WOProtocolStub.h"
35 #pragma mark C function implementations
37 NSString *WOStringFromProtocol(Protocol *aProtocol)
39 if (aProtocol == NULL) return nil;
40 return [NSString stringWithUTF8String:protocol_getName(aProtocol)];
43 @implementation WOProtocolMock
48 + (id)mockForProtocol:(Protocol *)aProtocol
50 NSParameterAssert(aProtocol != NULL);
51 return [[self alloc] initWithProtocol:aProtocol];
54 - (id)initWithProtocol:(Protocol *)aProtocol
56 NSParameterAssert(aProtocol != NULL);
58 // TODO: test for validity of the Protocol by checking with the runtime
60 if ((self = [super init]))
61 [self setMockedProtocol:aProtocol];
66 #pragma mark Recording
70 WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
71 [accepted addObject:stub];
77 WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
78 [acceptedOnce addObject:stub];
84 WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
85 [rejected addObject:stub];
91 WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
92 [expected addObject:stub];
98 WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
99 [expectedOnce addObject:stub];
105 WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
106 [expectedInOrder addObject:stub];
111 #pragma mark Proxy methods
113 - (void)forwardInvocation:(NSInvocation *)anInvocation
115 NSParameterAssert(anInvocation != nil);
117 // check if in reject list
118 for (WOStub *stub in rejected)
119 if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
121 [NSException raise:NSInternalInconsistencyException format:@"Rejected selector %@ for protocol %@",
122 NSStringFromSelector([anInvocation selector]), WOStringFromProtocol([self mockedProtocol])];
126 // check if expectedInOrder
127 for (WOStub *stub in expectedInOrder)
128 if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
130 NSAssert(([expectedInOrder objectAtIndex:0] != stub), @"invocation received out of order");
132 // if in order, remove from head of list
133 [expectedInOrder removeObjectAtIndex:0];
135 // and move to "accepted"
136 [accepted addObject:stub];
137 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
138 if ([stub exception]) @throw [stub exception];
142 // check if expected once
143 for (WOStub *stub in expectedOnce)
144 if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
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];
155 for (WOStub *stub in expected)
156 if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
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];
166 // check if accepted once
167 for (WOStub *stub in acceptedOnce)
168 if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
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];
179 for (WOStub *stub in accepted)
180 if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
182 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
183 if ([stub exception]) @throw [stub exception];
188 if ([self acceptsByDefault]) return;
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])];
195 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
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;
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];
207 #pragma mark Accessors
209 - (Protocol *)mockedProtocol
211 return mockedProtocol;
214 - (void)setMockedProtocol:(Protocol *)aMockedProtocol
216 mockedProtocol = aMockedProtocol;