diff options
Diffstat (limited to 'third_party/ocmock/OCPartialMockObject.m')
-rw-r--r-- | third_party/ocmock/OCPartialMockObject.m | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/third_party/ocmock/OCPartialMockObject.m b/third_party/ocmock/OCPartialMockObject.m new file mode 100644 index 0000000..4d35f13 --- /dev/null +++ b/third_party/ocmock/OCPartialMockObject.m @@ -0,0 +1,124 @@ +//--------------------------------------------------------------------------------------- +// $Id: $ +// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. +//--------------------------------------------------------------------------------------- + +#import <objc/objc-runtime.h> +#import "OCPartialMockRecorder.h" +#import "OCPartialMockObject.h" + + +@implementation OCPartialMockObject + + +#pragma mark Mock table + +static NSMutableDictionary *mockTable; + ++ (void)initialize +{ + if(self == [OCPartialMockObject class]) + mockTable = [[NSMutableDictionary alloc] init]; +} + ++ (void)rememberPartialMock:(OCPartialMockObject *)mock forObject:(id)anObject +{ + [mockTable setObject:mock forKey:[NSValue valueWithNonretainedObject:anObject]]; +} + ++ (void)forgetPartialMockForObject:(id)anObject +{ + [mockTable removeObjectForKey:[NSValue valueWithNonretainedObject:anObject]]; +} + ++ (OCPartialMockObject *)partialMockForObject:(id)anObject +{ + OCPartialMockObject *mock = [mockTable objectForKey:[NSValue valueWithNonretainedObject:anObject]]; + if(mock == nil) + [NSException raise:NSInternalInconsistencyException format:@"No partial mock for object %p", anObject]; + return mock; +} + + + +#pragma mark Initialisers, description, accessors, etc. + +- (id)initWithObject:(NSObject *)anObject +{ + [super initWithClass:[anObject class]]; + realObject = [anObject retain]; + [[self class] rememberPartialMock:self forObject:anObject]; + [self setupSubclassForObject:realObject]; + return self; +} + +- (void)dealloc +{ + object_setClass(realObject, [self mockedClass]); + [realObject release]; + [[self class] forgetPartialMockForObject:realObject]; + [super dealloc]; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"OCPartialMockObject[%@]", NSStringFromClass(mockedClass)]; +} + +- (NSObject *)realObject +{ + return realObject; +} + + + +#pragma mark Subclass management + +- (void)setupSubclassForObject:(id)anObject +{ + Class realClass = [anObject class]; + const char *className = [[NSString stringWithFormat:@"%@-%p", realClass, anObject] cString]; + NSLog(@"Creating class named %s", className); + Class subclass = objc_allocateClassPair(realClass, className, 0); + objc_registerClassPair(subclass); + object_setClass(anObject, subclass); + + Method forwardInvocationMethod = class_getInstanceMethod([self class], @selector(forwardInvocationForRealObject:)); + IMP forwardInvocationImp = method_getImplementation(forwardInvocationMethod); + const char *forwardInvocationTypes = method_getTypeEncoding(forwardInvocationMethod); + class_addMethod(subclass, @selector(forwardInvocation:), forwardInvocationImp, forwardInvocationTypes); +} + +- (void)setupForwarderForSelector:(SEL)selector +{ + Class subclass = [[self realObject] class]; + Method originalMethod = class_getInstanceMethod(subclass, selector); + IMP forwarderImp = [subclass instanceMethodForSelector:@selector(aMethodThatMustNotExist)]; + class_addMethod(subclass, method_getName(originalMethod), forwarderImp, method_getTypeEncoding(originalMethod)); +} + +- (void)forwardInvocationForRealObject:(NSInvocation *)anInvocation +{ + // in here "self" is a reference to the real object, not the mock + OCPartialMockObject *mock = [OCPartialMockObject partialMockForObject:self]; + if([mock handleInvocation:anInvocation] == NO) + [NSException raise:NSInternalInconsistencyException format:@"Ended up in subclass forwarder for %@ with unstubbed method %@", + [self class], NSStringFromSelector([anInvocation selector])]; +} + + + +#pragma mark Overrides + +- (id)getNewRecorder +{ + return [[[OCPartialMockRecorder alloc] initWithSignatureResolver:self] autorelease]; +} + +- (void)handleUnRecordedInvocation:(NSInvocation *)anInvocation +{ + [anInvocation invokeWithTarget:realObject]; +} + + +@end |