Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(31)

Side by Side Diff: third_party/ocmock/OCMock/OCPartialMockObject.m

Issue 2624143003: Update OCMock to 3.1.5 (Closed)
Patch Set: Patches in more commits Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 //------------------------------------------------------------------------------ --------- 1 /*
2 // $Id$ 2 * Copyright (c) 2009-2015 Erik Doernenburg and contributors
3 // Copyright (c) 2009 by Mulle Kybernetik. See License file for details. 3 *
4 //------------------------------------------------------------------------------ --------- 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may
5 * not use these files except in compliance with the License. You may obtain
6 * a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
5 16
6 #import <objc/runtime.h> 17 #import <objc/runtime.h>
7 #import "OCPartialMockRecorder.h" 18 #import "OCMockObject.h"
8 #import "OCPartialMockObject.h" 19 #import "OCPartialMockObject.h"
20 #import "NSMethodSignature+OCMAdditions.h"
21 #import "NSObject+OCMAdditions.h"
22 #import "OCMFunctions.h"
23 #import "OCMInvocationStub.h"
9 24
10 25
11 @interface OCPartialMockObject (Private)
12 - (void)forwardInvocationForRealObject:(NSInvocation *)anInvocation;
13 @end
14
15
16 NSString *OCMRealMethodAliasPrefix = @"ocmock_replaced_";
17
18 @implementation OCPartialMockObject 26 @implementation OCPartialMockObject
19 27
20
21 #pragma mark Mock table
22
23 static NSMutableDictionary *mockTable;
24
25 + (void)initialize
26 {
27 if(self == [OCPartialMockObject class])
28 mockTable = [[NSMutableDictionary alloc] init];
29 }
30
31 + (void)rememberPartialMock:(OCPartialMockObject *)mock forObject:(id)anObject
32 {
33 [mockTable setObject:[NSValue valueWithNonretainedObject:mock] forKey:[N SValue valueWithNonretainedObject:anObject]];
34 }
35
36 + (void)forgetPartialMockForObject:(id)anObject
37 {
38 [mockTable removeObjectForKey:[NSValue valueWithNonretainedObject:anObje ct]];
39 }
40
41 + (OCPartialMockObject *)existingPartialMockForObject:(id)anObject
42 {
43 OCPartialMockObject *mock = [[mockTable objectForKey:[NSValue valueWithN onretainedObject:anObject]] nonretainedObjectValue];
44 if(mock == nil)
45 [NSException raise:NSInternalInconsistencyException format:@"No partial mock for object %p", anObject];
46 return mock;
47 }
48
49
50
51 #pragma mark Initialisers, description, accessors, etc. 28 #pragma mark Initialisers, description, accessors, etc.
52 29
53 - (id)initWithObject:(NSObject *)anObject 30 - (id)initWithObject:(NSObject *)anObject
54 { 31 {
32 NSParameterAssert(anObject != nil);
33 [self assertClassIsSupported:[anObject class]];
55 [super initWithClass:[anObject class]]; 34 [super initWithClass:[anObject class]];
56 realObject = [anObject retain]; 35 realObject = [anObject retain];
57 » [[self class] rememberPartialMock:self forObject:anObject]; 36 [self prepareObjectForInstanceMethodMocking];
58 » [self setupSubclassForObject:realObject];
59 return self; 37 return self;
60 } 38 }
61 39
62 - (void)dealloc 40 - (void)dealloc
63 { 41 {
64 » if(realObject != nil) 42 » [self stopMocking];
65 » » [self stop]; 43 » [realObject release];
66 [super dealloc]; 44 [super dealloc];
67 } 45 }
68 46
69 - (NSString *)description 47 - (NSString *)description
70 { 48 {
71 » return [NSString stringWithFormat:@"OCPartialMockObject[%@]", NSStringFr omClass(mockedClass)]; 49 » return [NSString stringWithFormat:@"OCPartialMockObject(%@)", NSStringFr omClass(mockedClass)];
72 } 50 }
73 51
74 - (NSObject *)realObject 52 - (NSObject *)realObject
75 { 53 {
76 return realObject; 54 return realObject;
77 } 55 }
78 56
79 - (void)stop 57 #pragma mark Helper methods
58
59 - (void)assertClassIsSupported:(Class)class
80 { 60 {
81 » object_setClass(realObject, [self mockedClass]); 61 NSString *classname = NSStringFromClass(class);
82 » [realObject release]; 62 NSString *reason = nil;
83 » [[self class] forgetPartialMockForObject:realObject]; 63 if([classname hasPrefix:@"__NSTagged"] || [classname hasPrefix:@"NSTagged"])
84 » realObject = nil; 64 reason = [NSString stringWithFormat:@"OCMock does not support partially mocking tagged classes; got %@", classname];
65 else if([classname hasPrefix:@"__NSCF"])
66 reason = [NSString stringWithFormat:@"OCMock does not support partially mocking toll-free bridged classes; got %@", classname];
67
68 if(reason != nil)
69 [[NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil] raise];
85 } 70 }
86 71
87 72
88 #pragma mark Subclass management 73 #pragma mark Extending/overriding superclass behaviour
89 74
90 - (void)setupSubclassForObject:(id)anObject 75 - (void)stopMocking
91 { 76 {
92 » Class realClass = [anObject class]; 77 if(realObject != nil)
93 » double timestamp = [NSDate timeIntervalSinceReferenceDate]; 78 {
94 » const char *className = [[NSString stringWithFormat:@"%@-%p-%f", realCla ss, anObject, timestamp] UTF8String]; 79 OCMSetAssociatedMockForObject(nil, realObject);
95 » Class subclass = objc_allocateClassPair(realClass, className, 0); 80 object_setClass(realObject, [self mockedClass]);
96 » objc_registerClassPair(subclass); 81 [realObject release];
97 » object_setClass(anObject, subclass); 82 realObject = nil;
98 » 83 }
99 » Method forwardInvocationMethod = class_getInstanceMethod([self class], @ selector(forwardInvocationForRealObject:)); 84 [super stopMocking];
100 » IMP forwardInvocationImp = method_getImplementation(forwardInvocationMet hod);
101 » const char *forwardInvocationTypes = method_getTypeEncoding(forwardInvoc ationMethod);
102 » class_addMethod(subclass, @selector(forwardInvocation:), forwardInvocati onImp, forwardInvocationTypes);
103 } 85 }
104 86
105 - (void)setupForwarderForSelector:(SEL)selector 87 - (void)addStub:(OCMInvocationStub *)aStub
106 { 88 {
107 » Class subclass = [[self realObject] class]; 89 [super addStub:aStub];
108 » Method originalMethod = class_getInstanceMethod([subclass superclass], s elector); 90 if(![aStub recordedAsClassMethod])
109 » IMP originalImp = method_getImplementation(originalMethod); 91 [self setupForwarderForSelector:[[aStub recordedInvocation] selector]];
110
111 » IMP forwarderImp = [subclass instanceMethodForSelector:@selector(aMethod ThatMustNotExist)];
112 » class_addMethod(subclass, method_getName(originalMethod), forwarderImp, method_getTypeEncoding(originalMethod));
113
114 » SEL aliasSelector = NSSelectorFromString([OCMRealMethodAliasPrefix strin gByAppendingString:NSStringFromSelector(selector)]);
115 » class_addMethod(subclass, aliasSelector, originalImp, method_getTypeEnco ding(originalMethod));
116 }
117
118 - (void)forwardInvocationForRealObject:(NSInvocation *)anInvocation
119 {
120 » // in here "self" is a reference to the real object, not the mock
121 » OCPartialMockObject *mock = [OCPartialMockObject existingPartialMockForO bject:self];
122 » if([mock handleInvocation:anInvocation] == NO)
123 » » [NSException raise:NSInternalInconsistencyException format:@"End ed up in subclass forwarder for %@ with unstubbed method %@",
124 » » [self class], NSStringFromSelector([anInvocation selector])];
125 }
126
127
128
129 #pragma mark Overrides
130
131 - (id)getNewRecorder
132 {
133 » return [[[OCPartialMockRecorder alloc] initWithSignatureResolver:self] a utorelease];
134 } 92 }
135 93
136 - (void)handleUnRecordedInvocation:(NSInvocation *)anInvocation 94 - (void)handleUnRecordedInvocation:(NSInvocation *)anInvocation
137 { 95 {
138 [anInvocation invokeWithTarget:realObject]; 96 [anInvocation invokeWithTarget:realObject];
139 } 97 }
140 98
141 99
100 #pragma mark Subclass management
101
102 - (void)prepareObjectForInstanceMethodMocking
103 {
104 OCMSetAssociatedMockForObject(self, realObject);
105
106 /* dynamically create a subclass and set it as the class of the object */
107 Class subclass = OCMCreateSubclass(mockedClass, realObject);
108 object_setClass(realObject, subclass);
109
110 /* point forwardInvocation: of the object to the implementation in the mock */
111 Method myForwardMethod = class_getInstanceMethod([self mockObjectClass], @selector(forwardInvocationForRealObject:));
112 IMP myForwardIMP = method_getImplementation(myForwardMethod);
113 class_addMethod(subclass, @selector(forwardInvocation:), myForwardIMP, metho d_getTypeEncoding(myForwardMethod));
114
115 /* do the same for forwardingTargetForSelector, remember existing imp with a lias selector */
116 Method myForwardingTargetMethod = class_getInstanceMethod([self mockObjectCl ass], @selector(forwardingTargetForSelectorForRealObject:));
117 IMP myForwardingTargetIMP = method_getImplementation(myForwardingTargetMetho d);
118 IMP originalForwardingTargetIMP = [mockedClass instanceMethodForSelector:@se lector(forwardingTargetForSelector:)];
119 class_addMethod(subclass, @selector(forwardingTargetForSelector:), myForward ingTargetIMP, method_getTypeEncoding(myForwardingTargetMethod));
120 class_addMethod(subclass, @selector(ocmock_replaced_forwardingTargetForSelec tor:), originalForwardingTargetIMP, method_getTypeEncoding(myForwardingTargetMet hod));
121
122 /* We also override the -class method to return the original class */
123 Method myObjectClassMethod = class_getInstanceMethod([self mockObjectClass], @selector(classForRealObject));
124 const char *objectClassTypes = method_getTypeEncoding(myObjectClassMethod);
125 IMP myObjectClassImp = method_getImplementation(myObjectClassMethod);
126 class_addMethod(subclass, @selector(class), myObjectClassImp, objectClassTyp es);
127
128 /* Adding forwarder for most instance methods to allow for verify after run */
129 NSArray *methodBlackList = @[@"class", @"forwardingTargetForSelector:", @"me thodSignatureForSelector:", @"forwardInvocation:",
130 @"allowsWeakReference", @"retainWeakReference", @"isBlock"];
131 [NSObject enumerateMethodsInClass:mockedClass usingBlock:^(Class cls, SEL se l) {
132 if((cls == [NSObject class]) || (cls == [NSProxy class]))
133 return;
134 NSString *className = NSStringFromClass(cls);
135 NSString *selName = NSStringFromSelector(sel);
136 if(([className hasPrefix:@"NS"] || [className hasPrefix:@"UI"]) &&
137 ([selName hasPrefix:@"_"] || [selName hasSuffix:@"_"]))
138 return;
139 if([methodBlackList containsObject:selName])
140 return;
141 @try
142 {
143 [self setupForwarderForSelector:sel];
144 }
145 @catch(NSException *e)
146 {
147 // ignore for now
148 }
149 }];
150 }
151
152 - (void)setupForwarderForSelector:(SEL)sel
153 {
154 SEL aliasSelector = OCMAliasForOriginalSelector(sel);
155 if(class_getInstanceMethod(object_getClass(realObject), aliasSelector) != NU LL)
156 return;
157
158 Method originalMethod = class_getInstanceMethod(mockedClass, sel);
159 IMP originalIMP = method_getImplementation(originalMethod);
160 const char *types = method_getTypeEncoding(originalMethod);
161 /* Might be NULL if the selector is forwarded to another class */
162 // TODO: check the fallback implementation is actually sufficient
163 if(types == NULL)
164 types = ([[mockedClass instanceMethodSignatureForSelector:sel] fullObjCT ypes]);
165
166 Class subclass = object_getClass([self realObject]);
167 IMP forwarderIMP = [mockedClass instanceMethodForwarderForSelector:sel];
168 class_replaceMethod(subclass, sel, forwarderIMP, types);
169 class_addMethod(subclass, aliasSelector, originalIMP, types);
170 }
171
172
173 // Implementation of the -class method; return the Class that was reported with [realObject class] prior to mocking
174 - (Class)classForRealObject
175 {
176 // in here "self" is a reference to the real object, not the mock
177 OCPartialMockObject *mock = OCMGetAssociatedMockForObject(self);
178 if(mock == nil)
179 [NSException raise:NSInternalInconsistencyException format:@"No partial mock for object %p", self];
180 return [mock mockedClass];
181 }
182
183
184 - (id)forwardingTargetForSelectorForRealObject:(SEL)sel
185 {
186 // in here "self" is a reference to the real object, not the mock
187 OCPartialMockObject *mock = OCMGetAssociatedMockForObject(self);
188 if(mock == nil)
189 [NSException raise:NSInternalInconsistencyException format:@"No partial mock for object %p", self];
190 if([mock handleSelector:sel])
191 return self;
192
193 return [self ocmock_replaced_forwardingTargetForSelector:sel];
194 }
195
196 // Make the compiler happy in -forwardingTargetForSelectorForRealObject: becaus e it can't find the message…
197 - (id)ocmock_replaced_forwardingTargetForSelector:(SEL)sel
198 {
199 return nil;
200 }
201
202
203 - (void)forwardInvocationForRealObject:(NSInvocation *)anInvocation
204 {
205 // in here "self" is a reference to the real object, not the mock
206 OCPartialMockObject *mock = OCMGetAssociatedMockForObject(self);
207 if(mock == nil)
208 [NSException raise:NSInternalInconsistencyException format:@"No partial mock for object %p", self];
209
210 if([mock handleInvocation:anInvocation] == NO)
211 {
212 [anInvocation setSelector:OCMAliasForOriginalSelector([anInvocation sele ctor])];
213 [anInvocation invoke];
214 }
215 }
216
217
142 @end 218 @end
OLDNEW
« no previous file with comments | « third_party/ocmock/OCMock/OCPartialMockObject.h ('k') | third_party/ocmock/OCMock/OCPartialMockRecorder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698