| Index: third_party/ocmock/OCMock/OCClassMockObject.m
|
| diff --git a/third_party/ocmock/OCMock/OCClassMockObject.m b/third_party/ocmock/OCMock/OCClassMockObject.m
|
| index 65b8fdf02d52317d7b617a5628d49dc622e784f6..4dda97ab4688595308e2091b8ab54e9368214a2d 100644
|
| --- a/third_party/ocmock/OCMock/OCClassMockObject.m
|
| +++ b/third_party/ocmock/OCMock/OCClassMockObject.m
|
| @@ -1,10 +1,24 @@
|
| -//---------------------------------------------------------------------------------------
|
| -// $Id$
|
| -// Copyright (c) 2005-2008 by Mulle Kybernetik. See License file for details.
|
| -//---------------------------------------------------------------------------------------
|
| +/*
|
| + * Copyright (c) 2005-2015 Erik Doernenburg and contributors
|
| + *
|
| + * Licensed under the Apache License, Version 2.0 (the "License"); you may
|
| + * not use these files except in compliance with the License. You may obtain
|
| + * a copy of the License at
|
| + *
|
| + * http://www.apache.org/licenses/LICENSE-2.0
|
| + *
|
| + * Unless required by applicable law or agreed to in writing, software
|
| + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
| + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
| + * License for the specific language governing permissions and limitations
|
| + * under the License.
|
| + */
|
|
|
| +#import <objc/runtime.h>
|
| #import "OCClassMockObject.h"
|
| -
|
| +#import "NSObject+OCMAdditions.h"
|
| +#import "OCMFunctions.h"
|
| +#import "OCMInvocationStub.h"
|
|
|
| @implementation OCClassMockObject
|
|
|
| @@ -12,14 +26,22 @@
|
|
|
| - (id)initWithClass:(Class)aClass
|
| {
|
| - [super init];
|
| - mockedClass = aClass;
|
| - return self;
|
| + NSParameterAssert(aClass != nil);
|
| + [super init];
|
| + mockedClass = aClass;
|
| + [self prepareClassForClassMethodMocking];
|
| + return self;
|
| +}
|
| +
|
| +- (void)dealloc {
|
| + [self stopMocking];
|
| + [super dealloc];
|
| }
|
|
|
| - (NSString *)description
|
| {
|
| - return [NSString stringWithFormat:@"OCMockObject[%@]", NSStringFromClass(mockedClass)];
|
| + return [NSString
|
| + stringWithFormat:@"OCMockObject(%@)", NSStringFromClass(mockedClass)];
|
| }
|
|
|
| - (Class)mockedClass
|
| @@ -27,12 +49,145 @@
|
| return mockedClass;
|
| }
|
|
|
| +#pragma mark Extending/overriding superclass behaviour
|
| +
|
| +- (void)stopMocking {
|
| + if (originalMetaClass != nil)
|
| + [self restoreMetaClass];
|
| + [super stopMocking];
|
| +}
|
| +
|
| +- (void)restoreMetaClass {
|
| + OCMSetAssociatedMockForClass(nil, mockedClass);
|
| + OCMSetIsa(mockedClass, originalMetaClass);
|
| + originalMetaClass = nil;
|
| +}
|
| +
|
| +- (void)addStub:(OCMInvocationStub*)aStub {
|
| + [super addStub:aStub];
|
| + if ([aStub recordedAsClassMethod])
|
| + [self setupForwarderForClassMethodSelector:[[aStub recordedInvocation]
|
| + selector]];
|
| +}
|
| +
|
| +#pragma mark Class method mocking
|
| +
|
| +- (void)prepareClassForClassMethodMocking {
|
| + /* haven't figured out how to work around runtime dependencies on NSString, so
|
| + * exclude it for now */
|
| + /* also weird: [[NSString class] isKindOfClass:[NSString class]] is false,
|
| + * hence the additional clause */
|
| + if ([[mockedClass class] isKindOfClass:[NSString class]] ||
|
| + (mockedClass == [NSString class]))
|
| + return;
|
| +
|
| + /* if there is another mock for this exact class, stop it */
|
| + id otherMock = OCMGetAssociatedMockForClass(mockedClass, NO);
|
| + if (otherMock != nil)
|
| + [otherMock restoreMetaClass];
|
| +
|
| + OCMSetAssociatedMockForClass(self, mockedClass);
|
| +
|
| + /* dynamically create a subclass and use its meta class as the meta class for
|
| + * the mocked class */
|
| + Class subclass = OCMCreateSubclass(mockedClass, mockedClass);
|
| + originalMetaClass = object_getClass(mockedClass);
|
| + id newMetaClass = object_getClass(subclass);
|
| + OCMSetIsa(mockedClass, OCMGetIsa(subclass));
|
| +
|
| + /* point forwardInvocation: of the object to the implementation in the mock */
|
| + Method myForwardMethod = class_getInstanceMethod(
|
| + [self mockObjectClass], @selector(forwardInvocationForClassObject:));
|
| + IMP myForwardIMP = method_getImplementation(myForwardMethod);
|
| + class_addMethod(newMetaClass, @selector(forwardInvocation:), myForwardIMP,
|
| + method_getTypeEncoding(myForwardMethod));
|
| +
|
| + /* create a dummy initialize method */
|
| + Method myDummyInitializeMethod = class_getInstanceMethod(
|
| + [self mockObjectClass], @selector(initializeForClassObject));
|
| + const char* initializeTypes = method_getTypeEncoding(myDummyInitializeMethod);
|
| + IMP myDummyInitializeIMP = method_getImplementation(myDummyInitializeMethod);
|
| + class_addMethod(newMetaClass, @selector(initialize), myDummyInitializeIMP,
|
| + initializeTypes);
|
| +
|
| + /* adding forwarder for most class methods (instance methods on meta class) to
|
| + * allow for verify after run */
|
| + NSArray* methodBlackList = @[
|
| + @"class", @"forwardingTargetForSelector:", @"methodSignatureForSelector:",
|
| + @"forwardInvocation:", @"isBlock", @"instanceMethodForwarderForSelector:",
|
| + @"instanceMethodSignatureForSelector:"
|
| + ];
|
| + [NSObject
|
| + enumerateMethodsInClass:originalMetaClass
|
| + usingBlock:^(Class cls, SEL sel) {
|
| + if ((cls == object_getClass([NSObject class])) ||
|
| + (cls == [NSObject class]) ||
|
| + (cls == object_getClass(cls)))
|
| + return;
|
| + NSString* className = NSStringFromClass(cls);
|
| + NSString* selName = NSStringFromSelector(sel);
|
| + if (([className hasPrefix:@"NS"] ||
|
| + [className hasPrefix:@"UI"]) &&
|
| + ([selName hasPrefix:@"_"] || [selName hasSuffix:@"_"]))
|
| + return;
|
| + if ([methodBlackList containsObject:selName])
|
| + return;
|
| + @try {
|
| + [self setupForwarderForClassMethodSelector:sel];
|
| + } @catch (NSException* e) {
|
| + // ignore for now
|
| + }
|
| + }];
|
| +}
|
| +
|
| +- (void)setupForwarderForClassMethodSelector:(SEL)selector {
|
| + SEL aliasSelector = OCMAliasForOriginalSelector(selector);
|
| + if (class_getClassMethod(mockedClass, aliasSelector) != NULL)
|
| + return;
|
| +
|
| + Method originalMethod = class_getClassMethod(mockedClass, selector);
|
| + IMP originalIMP = method_getImplementation(originalMethod);
|
| + const char* types = method_getTypeEncoding(originalMethod);
|
| +
|
| + Class metaClass = object_getClass(mockedClass);
|
| + IMP forwarderIMP =
|
| + [originalMetaClass instanceMethodForwarderForSelector:selector];
|
| + class_replaceMethod(metaClass, selector, forwarderIMP, types);
|
| + class_addMethod(metaClass, aliasSelector, originalIMP, types);
|
| +}
|
| +
|
| +- (void)forwardInvocationForClassObject:(NSInvocation*)anInvocation {
|
| + // in here "self" is a reference to the real class, not the mock
|
| + OCClassMockObject* mock = OCMGetAssociatedMockForClass((Class)self, YES);
|
| + if (mock == nil) {
|
| + [NSException raise:NSInternalInconsistencyException
|
| + format:@"No mock for class %@", NSStringFromClass((Class)self)];
|
| + }
|
| + if ([mock handleInvocation:anInvocation] == NO) {
|
| + [anInvocation
|
| + setSelector:OCMAliasForOriginalSelector([anInvocation selector])];
|
| + [anInvocation invoke];
|
| + }
|
| +}
|
| +
|
| +- (void)initializeForClassObject {
|
| + // we really just want to have an implementation so that the superclass's is
|
| + // not called
|
| +}
|
|
|
| #pragma mark Proxy API
|
|
|
| - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
|
| {
|
| - return [mockedClass instanceMethodSignatureForSelector:aSelector];
|
| + return [mockedClass instanceMethodSignatureForSelector:aSelector];
|
| +}
|
| +
|
| +- (Class)mockObjectClass {
|
| + return [super class];
|
| +}
|
| +
|
| +- (Class) class {
|
| + return mockedClass;
|
| }
|
|
|
| - (BOOL)respondsToSelector:(SEL)selector
|
| @@ -40,4 +195,76 @@
|
| return [mockedClass instancesRespondToSelector:selector];
|
| }
|
|
|
| +- (BOOL)isKindOfClass:(Class)aClass {
|
| + return [mockedClass isSubclassOfClass:aClass];
|
| +}
|
| +
|
| +- (BOOL)conformsToProtocol:(Protocol*)aProtocol {
|
| + return class_conformsToProtocol(mockedClass, aProtocol);
|
| +}
|
| +
|
| +@end
|
| +
|
| +#pragma mark -
|
| +
|
| +/**
|
| + taken from:
|
| + `class-dump -f isNS
|
| + /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/System/Library/Frameworks/CoreFoundation.framework`
|
| +
|
| + @interface NSObject (__NSIsKinds)
|
| + - (_Bool)isNSValue__;
|
| + - (_Bool)isNSTimeZone__;
|
| + - (_Bool)isNSString__;
|
| + - (_Bool)isNSSet__;
|
| + - (_Bool)isNSOrderedSet__;
|
| + - (_Bool)isNSNumber__;
|
| + - (_Bool)isNSDictionary__;
|
| + - (_Bool)isNSDate__;
|
| + - (_Bool)isNSData__;
|
| + - (_Bool)isNSArray__;
|
| + */
|
| +
|
| +@implementation OCClassMockObject (NSIsKindsImplementation)
|
| +
|
| +- (BOOL)isNSValue__ {
|
| + return [mockedClass isKindOfClass:[NSValue class]];
|
| +}
|
| +
|
| +- (BOOL)isNSTimeZone__ {
|
| + return [mockedClass isKindOfClass:[NSTimeZone class]];
|
| +}
|
| +
|
| +- (BOOL)isNSSet__ {
|
| + return [mockedClass isKindOfClass:[NSSet class]];
|
| +}
|
| +
|
| +- (BOOL)isNSOrderedSet__ {
|
| + return [mockedClass isKindOfClass:[NSOrderedSet class]];
|
| +}
|
| +
|
| +- (BOOL)isNSNumber__ {
|
| + return [mockedClass isKindOfClass:[NSNumber class]];
|
| +}
|
| +
|
| +- (BOOL)isNSDate__ {
|
| + return [mockedClass isKindOfClass:[NSDate class]];
|
| +}
|
| +
|
| +- (BOOL)isNSString__ {
|
| + return [mockedClass isKindOfClass:[NSString class]];
|
| +}
|
| +
|
| +- (BOOL)isNSDictionary__ {
|
| + return [mockedClass isKindOfClass:[NSDictionary class]];
|
| +}
|
| +
|
| +- (BOOL)isNSData__ {
|
| + return [mockedClass isKindOfClass:[NSData class]];
|
| +}
|
| +
|
| +- (BOOL)isNSArray__ {
|
| + return [mockedClass isKindOfClass:[NSArray class]];
|
| +}
|
| +
|
| @end
|
|
|