1 //---------------------------------------------------------------------------------------
3 // Copyright (c) 2004-2011 by Mulle Kybernetik. See License file for details.
4 //---------------------------------------------------------------------------------------
6 #import <objc/runtime.h>
7 #import <OCMock/OCMockRecorder.h>
8 #import <OCMock/OCMArg.h>
9 #import <OCMock/OCMConstraint.h>
10 #import "OCMPassByRefSetter.h"
11 #import "OCMReturnValueProvider.h"
12 #import "OCMBoxedReturnValueProvider.h"
13 #import "OCMExceptionReturnValueProvider.h"
14 #import "OCMIndirectReturnValueProvider.h"
15 #import "OCMNotificationPoster.h"
16 #import "OCMBlockCaller.h"
17 #import "NSInvocation+OCMAdditions.h"
19 @interface NSObject(HCMatcherDummy)
20 - (BOOL)matches:(id)item;
26 @implementation OCMockRecorder
28 #pragma mark Initialisers, description, accessors, etc.
30 - (id)initWithSignatureResolver:(id)anObject
32 signatureResolver = anObject;
33 invocationHandlers = [[NSMutableArray alloc] init];
39 [recordedInvocation release];
40 [invocationHandlers release];
44 - (NSString *)description
46 return [recordedInvocation invocationDescription];
49 - (void)releaseInvocation
51 [recordedInvocation release];
52 recordedInvocation = nil;
56 #pragma mark Recording invocation handlers
58 - (id)andReturn:(id)anObject
60 [invocationHandlers addObject:[[[OCMReturnValueProvider alloc] initWithValue:anObject] autorelease]];
64 - (id)andReturnValue:(NSValue *)aValue
66 [invocationHandlers addObject:[[[OCMBoxedReturnValueProvider alloc] initWithValue:aValue] autorelease]];
70 - (id)andThrow:(NSException *)anException
72 [invocationHandlers addObject:[[[OCMExceptionReturnValueProvider alloc] initWithValue:anException] autorelease]];
76 - (id)andPost:(NSNotification *)aNotification
78 [invocationHandlers addObject:[[[OCMNotificationPoster alloc] initWithNotification:aNotification] autorelease]];
82 - (id)andCall:(SEL)selector onObject:(id)anObject
84 [invocationHandlers addObject:[[[OCMIndirectReturnValueProvider alloc] initWithProvider:anObject andSelector:selector] autorelease]];
88 #if NS_BLOCKS_AVAILABLE
90 - (id)andDo:(void (^)(NSInvocation *))aBlock
92 [invocationHandlers addObject:[[[OCMBlockCaller alloc] initWithCallBlock:aBlock] autorelease]];
98 - (id)andForwardToRealObject
100 [NSException raise:NSInternalInconsistencyException format:@"Method %@ can only be used with partial mocks.",
101 NSStringFromSelector(_cmd)];
102 return self; // keep compiler happy
106 - (NSArray *)invocationHandlers
108 return invocationHandlers;
112 #pragma mark Recording the actual invocation
114 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
116 return [signatureResolver methodSignatureForSelector:aSelector];
119 - (void)forwardInvocation:(NSInvocation *)anInvocation
121 if(recordedInvocation != nil)
122 [NSException raise:NSInternalInconsistencyException format:@"Recorder received two methods to record."];
123 [anInvocation setTarget:nil];
124 [anInvocation retainArguments];
125 recordedInvocation = [anInvocation retain];
130 #pragma mark Checking the invocation
132 - (BOOL)matchesInvocation:(NSInvocation *)anInvocation
134 id recordedArg, passedArg;
137 if([anInvocation selector] != [recordedInvocation selector])
140 n = (int)[[recordedInvocation methodSignature] numberOfArguments];
141 for(i = 2; i < n; i++)
143 recordedArg = [recordedInvocation getArgumentAtIndexAsObject:i];
144 passedArg = [anInvocation getArgumentAtIndexAsObject:i];
146 if([recordedArg isProxy])
148 if(![recordedArg isEqual:passedArg])
153 if([recordedArg isKindOfClass:[NSValue class]])
154 recordedArg = [OCMArg resolveSpecialValues:recordedArg];
156 if([recordedArg isKindOfClass:[OCMConstraint class]])
158 if([recordedArg evaluate:passedArg] == NO)
161 else if([recordedArg isKindOfClass:[OCMPassByRefSetter class]])
163 // side effect but easier to do here than in handleInvocation
164 *(id *)[passedArg pointerValue] = [(OCMPassByRefSetter *)recordedArg value];
166 else if([recordedArg conformsToProtocol:objc_getProtocol("HCMatcher")])
168 if([recordedArg matches:passedArg] == NO)
173 if(([recordedArg class] == [NSNumber class]) &&
174 ([(NSNumber*)recordedArg compare:(NSNumber*)passedArg] != NSOrderedSame))
176 if(([recordedArg isEqual:passedArg] == NO) &&
177 !((recordedArg == nil) && (passedArg == nil)))