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 "WOProtocolStub.h"
34 #pragma mark C function implementations
36 NSString *WOStringFromProtocol(Protocol *aProtocol)
38 if (aProtocol == NULL) return nil;
39 return [NSString stringWithUTF8String:protocol_getName(aProtocol)];
42 @implementation WOProtocolMock
47 + (id)mockForProtocol:(Protocol *)aProtocol
49 NSParameterAssert(aProtocol != NULL);
50 return [[self alloc] initWithProtocol:aProtocol];
53 - (id)initWithProtocol:(Protocol *)aProtocol
55 NSParameterAssert(aProtocol != NULL);
57 // TODO: test for validity of the Protocol by checking with the runtime
59 if ((self = [super init]))
60 [self setMockedProtocol:aProtocol];
65 #pragma mark Recording
69 WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
70 [accepted addObject:stub];
76 WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
77 [acceptedOnce addObject:stub];
83 WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
84 [rejected addObject:stub];
90 WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
91 [expected addObject:stub];
97 WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
98 [expectedOnce addObject:stub];
104 WOProtocolStub *stub = [WOProtocolStub stubForProtocol:[self mockedProtocol] withDelegate:self];
105 [expectedInOrder addObject:stub];
110 #pragma mark Proxy methods
112 - (void)forwardInvocation:(NSInvocation *)anInvocation
114 NSParameterAssert(anInvocation != nil);
116 // check if in reject list
117 for (WOStub *stub in rejected)
118 if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
120 [NSException raise:NSInternalInconsistencyException format:@"Rejected selector %@ for protocol %@",
121 NSStringFromSelector([anInvocation selector]), WOStringFromProtocol([self mockedProtocol])];
125 // check if expectedInOrder
126 for (WOStub *stub in expectedInOrder)
127 if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
129 NSAssert(([expectedInOrder objectAtIndex:0] != stub), @"invocation received out of order");
131 // if in order, remove from head of list
132 [expectedInOrder removeObjectAtIndex:0];
134 // and move to "accepted"
135 [accepted addObject:stub];
136 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
137 if ([stub exception]) @throw [stub exception];
141 // check if expected once
142 for (WOStub *stub in expectedOnce)
143 if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
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];
154 for (WOStub *stub in expected)
155 if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
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];
165 // check if accepted once
166 for (WOStub *stub in acceptedOnce)
167 if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
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];
178 for (WOStub *stub in accepted)
179 if ([anInvocation WOTest_isEqualToInvocation:[stub recordedInvocation]])
181 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
182 if ([stub exception]) @throw [stub exception];
187 if ([self acceptsByDefault]) return;
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])];
194 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
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;
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];
206 #pragma mark Accessors
208 - (Protocol *)mockedProtocol
210 return mockedProtocol;
213 - (void)setMockedProtocol:(Protocol *)aMockedProtocol
215 mockedProtocol = aMockedProtocol;