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

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

Issue 2410583002: Test update OCMock (Closed)
Patch Set: Patch in exactly 3.1.5 Created 3 years, 11 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"
9 20 #import "NSMethodSignature+OCMAdditions.h"
10 21 #import "NSObject+OCMAdditions.h"
11 @interface OCPartialMockObject (Private) 22 #import "OCMFunctions.h"
12 - (void)forwardInvocationForRealObject:(NSInvocation *)anInvocation; 23 #import "OCMInvocationStub.h"
13 @end
14
15
16 NSString *OCMRealMethodAliasPrefix = @"ocmock_replaced_";
17 24
18 @implementation OCPartialMockObject 25 @implementation OCPartialMockObject
19 26
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. 27 #pragma mark Initialisers, description, accessors, etc.
52 28
53 - (id)initWithObject:(NSObject *)anObject 29 - (id)initWithObject:(NSObject *)anObject
54 { 30 {
55 » [super initWithClass:[anObject class]]; 31 NSParameterAssert(anObject != nil);
56 » realObject = [anObject retain]; 32 [self assertClassIsSupported:[anObject class]];
57 » [[self class] rememberPartialMock:self forObject:anObject]; 33 [super initWithClass:[anObject class]];
58 » [self setupSubclassForObject:realObject]; 34 realObject = [anObject retain];
59 » return self; 35 [self prepareObjectForInstanceMethodMocking];
36 return self;
60 } 37 }
61 38
62 - (void)dealloc 39 - (void)dealloc
63 { 40 {
64 » if(realObject != nil) 41 [self stopMocking];
65 » » [self stop]; 42 [realObject release];
66 » [super dealloc]; 43 [super dealloc];
67 } 44 }
68 45
69 - (NSString *)description 46 - (NSString *)description
70 { 47 {
71 » return [NSString stringWithFormat:@"OCPartialMockObject[%@]", NSStringFr omClass(mockedClass)]; 48 return [NSString stringWithFormat:@"OCPartialMockObject(%@)",
49 NSStringFromClass(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
80 { 58
81 » object_setClass(realObject, [self mockedClass]); 59 - (void)assertClassIsSupported:(Class) class {
82 » [realObject release]; 60 NSString* classname = NSStringFromClass(class);
83 » [[self class] forgetPartialMockForObject:realObject]; 61 NSString* reason = nil;
84 » realObject = nil; 62 if ([classname hasPrefix:@"__NSTagged"] || [classname hasPrefix:@"NSTagged"])
63 reason = [NSString
64 stringWithFormat:
65 @"OCMock does not support partially mocking tagged classes; got %@",
66 classname];
67 else if ([classname hasPrefix:@"__NSCF"])
68 reason =
69 [NSString stringWithFormat:@"OCMock does not support partially mocking "
70 @"toll-free bridged classes; got %@",
71 classname];
72
73 if (reason != nil)
74 [[NSException exceptionWithName:NSInvalidArgumentException
75 reason:reason
76 userInfo:nil] raise];
77 }
78 #pragma mark Extending/overriding superclass behaviour
79
80 - (void)stopMocking {
81 if (realObject != nil) {
82 OCMSetAssociatedMockForObject(nil, realObject);
83 object_setClass(realObject, [self mockedClass]);
84 [realObject release];
85 realObject = nil;
86 }
87 [super stopMocking];
88 }
89
90 - (void)addStub:(OCMInvocationStub*)aStub {
91 [super addStub:aStub];
92 if (![aStub recordedAsClassMethod])
93 [self setupForwarderForSelector:[[aStub recordedInvocation] selector]];
94 }
95
96 - (void)handleUnRecordedInvocation:(NSInvocation*)anInvocation {
97 [anInvocation invokeWithTarget:realObject];
98 }
99
100 #pragma mark Subclass management
101
102 - (void)prepareObjectForInstanceMethodMocking {
103 OCMSetAssociatedMockForObject(self, realObject);
104
105 /* dynamically create a subclass and set it as the class of the object */
106 Class subclass = OCMCreateSubclass(mockedClass, realObject);
107 object_setClass(realObject, subclass);
108
109 /* point forwardInvocation: of the object to the implementation in the mock */
110 Method myForwardMethod = class_getInstanceMethod(
111 [self mockObjectClass], @selector(forwardInvocationForRealObject:));
112 IMP myForwardIMP = method_getImplementation(myForwardMethod);
113 class_addMethod(subclass, @selector(forwardInvocation:), myForwardIMP,
114 method_getTypeEncoding(myForwardMethod));
115
116 /* do the same for forwardingTargetForSelector, remember existing imp with
117 * alias selector */
118 Method myForwardingTargetMethod = class_getInstanceMethod(
119 [self mockObjectClass],
120 @selector(forwardingTargetForSelectorForRealObject:));
121 IMP myForwardingTargetIMP =
122 method_getImplementation(myForwardingTargetMethod);
123 IMP originalForwardingTargetIMP = [mockedClass
124 instanceMethodForSelector:@selector(forwardingTargetForSelector:)];
125 class_addMethod(subclass, @selector(forwardingTargetForSelector:),
126 myForwardingTargetIMP,
127 method_getTypeEncoding(myForwardingTargetMethod));
128 class_addMethod(subclass,
129 @selector(ocmock_replaced_forwardingTargetForSelector:),
130 originalForwardingTargetIMP,
131 method_getTypeEncoding(myForwardingTargetMethod));
132
133 /* We also override the -class method to return the original class */
134 Method myObjectClassMethod = class_getInstanceMethod(
135 [self mockObjectClass], @selector(classForRealObject));
136 const char* objectClassTypes = method_getTypeEncoding(myObjectClassMethod);
137 IMP myObjectClassImp = method_getImplementation(myObjectClassMethod);
138 class_addMethod(subclass, @selector(class), myObjectClassImp,
139 objectClassTypes);
140
141 /* Adding forwarder for most instance methods to allow for verify after run */
142 NSArray* methodBlackList = @[
143 @"class", @"forwardingTargetForSelector:", @"methodSignatureForSelector:",
144 @"forwardInvocation:", @"allowsWeakReference", @"retainWeakReference",
145 @"isBlock"
146 ];
147 [NSObject
148 enumerateMethodsInClass:mockedClass
149 usingBlock:^(Class cls, SEL sel) {
150 if ((cls == [NSObject class]) || (cls == [NSProxy class]))
151 return;
152 NSString* className = NSStringFromClass(cls);
153 NSString* selName = NSStringFromSelector(sel);
154 if (([className hasPrefix:@"NS"] ||
155 [className hasPrefix:@"UI"]) &&
156 ([selName hasPrefix:@"_"] || [selName hasSuffix:@"_"]))
157 return;
158 if ([methodBlackList containsObject:selName])
159 return;
160 @try {
161 [self setupForwarderForSelector:sel];
162 } @catch (NSException* e) {
163 // ignore for now
164 }
165 }];
166 }
167
168 - (void)setupForwarderForSelector:(SEL)sel {
169 SEL aliasSelector = OCMAliasForOriginalSelector(sel);
170 if (class_getInstanceMethod(object_getClass(realObject), aliasSelector) !=
171 NULL)
172 return;
173
174 Method originalMethod = class_getInstanceMethod(mockedClass, sel);
175 IMP originalIMP = method_getImplementation(originalMethod);
176 const char* types = method_getTypeEncoding(originalMethod);
177 /* Might be NULL if the selector is forwarded to another class */
178 // TODO: check the fallback implementation is actually sufficient
179 if (types == NULL)
180 types =
181 ([[mockedClass instanceMethodSignatureForSelector:sel] fullObjCTypes]);
182
183 Class subclass = object_getClass([self realObject]);
184 IMP forwarderIMP = [mockedClass instanceMethodForwarderForSelector:sel];
185 class_replaceMethod(subclass, sel, forwarderIMP, types);
186 class_addMethod(subclass, aliasSelector, originalIMP, types);
187 }
188
189 // Implementation of the -class method; return the Class that was reported with
190 // [realObject class] prior to mocking
191 - (Class)classForRealObject {
192 // in here "self" is a reference to the real object, not the mock
193 OCPartialMockObject* mock = OCMGetAssociatedMockForObject(self);
194 if (mock == nil)
195 [NSException raise:NSInternalInconsistencyException
196 format:@"No partial mock for object %p", self];
197 return [mock mockedClass];
198 }
199
200 - (id)forwardingTargetForSelectorForRealObject:(SEL)sel {
201 // in here "self" is a reference to the real object, not the mock
202 OCPartialMockObject* mock = OCMGetAssociatedMockForObject(self);
203 if (mock == nil)
204 [NSException raise:NSInternalInconsistencyException
205 format:@"No partial mock for object %p", self];
206 if ([mock handleSelector:sel])
207 return self;
208
209 return [self ocmock_replaced_forwardingTargetForSelector:sel];
210 }
211
212 // Make the compiler happy in -forwardingTargetForSelectorForRealObject:
213 // because it can't find the message…
214 - (id)ocmock_replaced_forwardingTargetForSelector:(SEL)sel {
215 return nil;
216 }
217
218 - (void)forwardInvocationForRealObject:(NSInvocation*)anInvocation {
219 // in here "self" is a reference to the real object, not the mock
220 OCPartialMockObject* mock = OCMGetAssociatedMockForObject(self);
221 if (mock == nil)
222 [NSException raise:NSInternalInconsistencyException
223 format:@"No partial mock for object %p", self];
224
225 if ([mock handleInvocation:anInvocation] == NO) {
226 [anInvocation
227 setSelector:OCMAliasForOriginalSelector([anInvocation selector])];
228 [anInvocation invoke];
229 }
85 } 230 }
86 231
87 232
88 #pragma mark Subclass management
89
90 - (void)setupSubclassForObject:(id)anObject
91 {
92 Class realClass = [anObject class];
93 double timestamp = [NSDate timeIntervalSinceReferenceDate];
94 const char *className = [[NSString stringWithFormat:@"%@-%p-%f", realCla ss, anObject, timestamp] UTF8String];
95 Class subclass = objc_allocateClassPair(realClass, className, 0);
96 objc_registerClassPair(subclass);
97 object_setClass(anObject, subclass);
98
99 Method forwardInvocationMethod = class_getInstanceMethod([self class], @ selector(forwardInvocationForRealObject:));
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 }
104
105 - (void)setupForwarderForSelector:(SEL)selector
106 {
107 Class subclass = [[self realObject] class];
108 Method originalMethod = class_getInstanceMethod([subclass superclass], s elector);
109 IMP originalImp = method_getImplementation(originalMethod);
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 }
135
136 - (void)handleUnRecordedInvocation:(NSInvocation *)anInvocation
137 {
138 [anInvocation invokeWithTarget:realObject];
139 }
140
141
142 @end 233 @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