• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//---------------------------------------------------------------------------------------
2//  $Id$
3//  Copyright (c) 2004-2009 by Mulle Kybernetik. See License file for details.
4//---------------------------------------------------------------------------------------
5
6#import <OCMock/OCMockObject.h>
7#import "OCClassMockObject.h"
8#import "OCProtocolMockObject.h"
9#import "OCPartialMockObject.h"
10#import "OCObserverMockObject.h"
11#import <OCMock/OCMockRecorder.h>
12#import "NSInvocation+OCMAdditions.h"
13
14@interface OCMockObject(Private)
15+ (id)_makeNice:(OCMockObject *)mock;
16- (NSString *)_recorderDescriptions:(BOOL)onlyExpectations;
17@end
18
19#pragma mark  -
20
21
22@implementation OCMockObject
23
24#pragma mark  Class initialisation
25
26+ (void)initialize
27{
28	if([[NSInvocation class] instanceMethodSignatureForSelector:@selector(getArgumentAtIndexAsObject:)] == NULL)
29		[NSException raise:NSInternalInconsistencyException format:@"** Expected method not present; the method getArgumentAtIndexAsObject: is not implemented by NSInvocation. If you see this exception it is likely that you are using the static library version of OCMock and your project is not configured correctly to load categories from static libraries. Did you forget to add the -force_load linker flag?"];
30}
31
32
33#pragma mark  Factory methods
34
35+ (id)mockForClass:(Class)aClass
36{
37	return [[[OCClassMockObject alloc] initWithClass:aClass] autorelease];
38}
39
40+ (id)mockForProtocol:(Protocol *)aProtocol
41{
42	return [[[OCProtocolMockObject alloc] initWithProtocol:aProtocol] autorelease];
43}
44
45+ (id)partialMockForObject:(NSObject *)anObject
46{
47	return [[[OCPartialMockObject alloc] initWithObject:anObject] autorelease];
48}
49
50
51+ (id)niceMockForClass:(Class)aClass
52{
53	return [self _makeNice:[self mockForClass:aClass]];
54}
55
56+ (id)niceMockForProtocol:(Protocol *)aProtocol
57{
58	return [self _makeNice:[self mockForProtocol:aProtocol]];
59}
60
61
62+ (id)_makeNice:(OCMockObject *)mock
63{
64	mock->isNice = YES;
65	return mock;
66}
67
68
69+ (id)observerMock
70{
71	return [[[OCObserverMockObject alloc] init] autorelease];
72}
73
74
75
76#pragma mark  Initialisers, description, accessors, etc.
77
78- (id)init
79{
80	// no [super init], we're inheriting from NSProxy
81	expectationOrderMatters = NO;
82	recorders = [[NSMutableArray alloc] init];
83	expectations = [[NSMutableArray alloc] init];
84	rejections = [[NSMutableArray alloc] init];
85	exceptions = [[NSMutableArray alloc] init];
86	return self;
87}
88
89- (void)dealloc
90{
91	[recorders release];
92	[expectations release];
93	[rejections	release];
94	[exceptions release];
95	[super dealloc];
96}
97
98- (NSString *)description
99{
100	return @"OCMockObject";
101}
102
103
104- (void)setExpectationOrderMatters:(BOOL)flag
105{
106    expectationOrderMatters = flag;
107}
108
109
110#pragma mark  Public API
111
112- (id)stub
113{
114	OCMockRecorder *recorder = [self getNewRecorder];
115	[recorders addObject:recorder];
116	return recorder;
117}
118
119
120- (id)expect
121{
122	OCMockRecorder *recorder = [self stub];
123	[expectations addObject:recorder];
124	return recorder;
125}
126
127
128- (id)reject
129{
130	OCMockRecorder *recorder = [self stub];
131	[rejections addObject:recorder];
132	return recorder;
133}
134
135
136- (void)verify
137{
138	if([expectations count] == 1)
139	{
140		[NSException raise:NSInternalInconsistencyException format:@"%@: expected method was not invoked: %@",
141			[self description], [[expectations objectAtIndex:0] description]];
142	}
143	if([expectations count] > 0)
144	{
145		[NSException raise:NSInternalInconsistencyException format:@"%@ : %ld expected methods were not invoked: %@",
146			[self description], (unsigned long)[expectations count], [self _recorderDescriptions:YES]];
147	}
148	if([exceptions count] > 0)
149	{
150		[[exceptions objectAtIndex:0] raise];
151	}
152}
153
154
155
156#pragma mark  Handling invocations
157
158- (void)forwardInvocation:(NSInvocation *)anInvocation
159{
160	if([self handleInvocation:anInvocation] == NO)
161		[self handleUnRecordedInvocation:anInvocation];
162}
163
164- (BOOL)handleInvocation:(NSInvocation *)anInvocation
165{
166	OCMockRecorder *recorder = nil;
167	unsigned int			   i;
168
169	for(i = 0; i < [recorders count]; i++)
170	{
171		recorder = [recorders objectAtIndex:i];
172		if([recorder matchesInvocation:anInvocation])
173			break;
174	}
175
176	if(i == [recorders count])
177		return NO;
178
179	if([rejections containsObject:recorder])
180	{
181		NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException reason:
182								  [NSString stringWithFormat:@"%@: explicitly disallowed method invoked: %@", [self description],
183								   [anInvocation invocationDescription]] userInfo:nil];
184		[exceptions addObject:exception];
185		[exception raise];
186	}
187
188	if([expectations containsObject:recorder])
189	{
190		if(expectationOrderMatters && ([expectations objectAtIndex:0] != recorder))
191		{
192			[NSException raise:NSInternalInconsistencyException	format:@"%@: unexpected method invoked: %@\n\texpected:\t%@",
193			 [self description], [recorder description], [[expectations objectAtIndex:0] description]];
194
195		}
196		[[recorder retain] autorelease];
197		[expectations removeObject:recorder];
198		[recorders removeObjectAtIndex:i];
199	}
200	[[recorder invocationHandlers] makeObjectsPerformSelector:@selector(handleInvocation:) withObject:anInvocation];
201
202	return YES;
203}
204
205- (void)handleUnRecordedInvocation:(NSInvocation *)anInvocation
206{
207	if(isNice == NO)
208	{
209		NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException reason:
210								  [NSString stringWithFormat:@"%@: unexpected method invoked: %@ %@",  [self description],
211								   [anInvocation invocationDescription], [self _recorderDescriptions:NO]] userInfo:nil];
212		[exceptions addObject:exception];
213		[exception raise];
214	}
215}
216
217
218#pragma mark  Helper methods
219
220- (id)getNewRecorder
221{
222	return [[[OCMockRecorder alloc] initWithSignatureResolver:self] autorelease];
223}
224
225
226- (NSString *)_recorderDescriptions:(BOOL)onlyExpectations
227{
228	NSMutableString *outputString = [NSMutableString string];
229
230	OCMockRecorder *currentObject;
231	NSEnumerator *recorderEnumerator = [recorders objectEnumerator];
232	while((currentObject = [recorderEnumerator nextObject]) != nil)
233	{
234		NSString *prefix;
235
236		if(onlyExpectations)
237		{
238			if(![expectations containsObject:currentObject])
239				continue;
240			prefix = @" ";
241		}
242		else
243		{
244			if ([expectations containsObject:currentObject])
245				prefix = @"expected: ";
246			else
247				prefix = @"stubbed: ";
248		}
249		[outputString appendFormat:@"\n\t%@\t%@", prefix, [currentObject description]];
250	}
251
252	return outputString;
253}
254
255
256@end
257