diff --git a/Cartfile.private b/Cartfile.private index 93a533f9..5db6765f 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,2 +1,2 @@ github "Quick/Nimble" -github "specta/expecta" +github "specta/expecta" "master" diff --git a/Classes/Core/KWBlockInvocation.h b/Classes/Core/KWBlockInvocation.h new file mode 100644 index 00000000..b9ef0066 --- /dev/null +++ b/Classes/Core/KWBlockInvocation.h @@ -0,0 +1,23 @@ +// +// NSBlockInvocation.h +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/27/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import "KiwiConfiguration.h" + +#import "NSInvocation+KiwiAdditions.h" + +@interface KWBlockInvocation : NSInvocation + +#pragma mark - Creating NSInvocation Objects + ++ (NSInvocation *)invocationWithTarget:(id)anObject; ++ (NSInvocation *)invocationWithTarget:(id)anObject messageArguments:(const void *)firstBytes, ...; ++ (NSInvocation *)invocationWithTarget:(id)anObject + firstArgument:(const void *)firstBytes + argumentList:(va_list)argumentList; + +@end diff --git a/Classes/Core/KWBlockInvocation.m b/Classes/Core/KWBlockInvocation.m new file mode 100644 index 00000000..e3affc08 --- /dev/null +++ b/Classes/Core/KWBlockInvocation.m @@ -0,0 +1,70 @@ +// +// KWBlockInvocation.m +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/27/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import "KWBlockInvocation.h" + +#import "KWObjCUtilities.h" +#import "NSMethodSignature+KiwiAdditions.h" +#import "KWProxyBlock.h" + +@implementation KWBlockInvocation + +#pragma mark - Creating NSInvocation Objects + ++ (NSInvocation *)invocationWithTarget:(id)anObject { + return [self invocationWithTarget:anObject messageArguments:nil]; +} + ++ (NSInvocation *)invocationWithTarget:(id)anObject messageArguments:(const void *)firstBytes, ... { + va_list argumentList; + va_start(argumentList, firstBytes); + + return [self invocationWithTarget:anObject + selector:@selector(_doNothing) + firstArgument:firstBytes + argumentList:argumentList]; +} + ++ (NSInvocation *)invocationWithTarget:(id)anObject + firstArgument:(const void *)firstBytes + argumentList:(va_list)argumentList +{ + return [self invocationWithTarget:anObject + selector:@selector(_doNothing) + firstArgument:firstBytes + argumentList:argumentList]; +} + ++ (NSInvocation *)invocationWithTarget:(id)anObject + selector:(SEL)aSelector + firstArgument:(const void *)firstBytes + argumentList:(va_list)argumentList +{ + if (![anObject isMemberOfClass:[KWProxyBlock class]]) { + [NSException raise:NSInvalidArgumentException format:@"%@ - target must be KWProxyBlock", anObject]; + } + + return [super invocationWithTarget:anObject + selector:@selector(_doNothing) + firstArgument:firstBytes + argumentList:argumentList]; +} + +#pragma mark - Argument Offset + +- (NSUInteger)argumentOffset { + return 1; +} + +#pragma mark - Properties + +- (void)_doNothing { + +} + +@end diff --git a/Classes/Core/KWBlockLayout.h b/Classes/Core/KWBlockLayout.h new file mode 100644 index 00000000..d3b92719 --- /dev/null +++ b/Classes/Core/KWBlockLayout.h @@ -0,0 +1,100 @@ +// +// KWBlockLibClosure.h +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/16/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import "KiwiConfiguration.h" + +// Block types and helper methods derived from +// http://opensource.apple.com//source/libclosure/libclosure-65/Block_private.h +// http://opensource.apple.com//source/libclosure/libclosure-65/runtime.c + +typedef enum { + kKWBlockDeallocating = (0x0001), // runtime + kKWBlockRefcountMask = (0xfffe), // runtime + kKWBlockNeedsFree = (1 << 24), // runtime + kKWBlockHasCopyDispose = (1 << 25), // compiler + kKWBlockHasCTOR = (1 << 26), // compiler: helpers have C++ code + kKWBlockIsGC = (1 << 27), // runtime + kKWBlockIsGlobal = (1 << 28), // compiler + kKWBlockHasStructureReturn = (1 << 29), // compiler: undefined if !kKWBlockHasSignature + kKWBlockHasSignature = (1 << 30), // compiler + kKWBlockHasExtendedLayout = (1 << 31) // compiler +} kKWBlockOptions; + +struct KWBlockDescriptor { + uintptr_t reserved; + uintptr_t size; +}; +typedef struct KWBlockDescriptor KWBlockDescriptor; + +struct KWBlockDescriptorCopyDispose { + // requires kKWBlockHasCopyDispose + void (*copy)(void *dst, const void *src); + void (*dispose)(const void *); +}; +typedef struct KWBlockDescriptorCopyDispose KWBlockDescriptorCopyDispose; + +struct KWBlockDescriptorMetadata { + // requires kKWBlockHasSignature + const char *signature; + const char *layout; // contents depend on kKWBlockHasExtendedLayout +}; +typedef struct KWBlockDescriptorMetadata KWBlockDescriptorMetadata; + +struct KWBlockLayout { + void *isa; + volatile int32_t flags; // contains ref count + int32_t reserved; + IMP imp; + KWBlockDescriptor *descriptor; +}; +typedef struct KWBlockLayout KWBlockLayout; + +FOUNDATION_EXPORT +kKWBlockOptions KWBlockLayoutGetFlags(KWBlockLayout *block); + +FOUNDATION_EXPORT +BOOL KWBlockLayoutGetOption(KWBlockLayout *block, kKWBlockOptions option); + +FOUNDATION_EXPORT +BOOL KWBlockLayoutHasCopyDispose(KWBlockLayout *block); + +FOUNDATION_EXPORT +BOOL KWBlockLayoutHasCTOR(KWBlockLayout *block); + +FOUNDATION_EXPORT +BOOL KWBlockLayoutIsGlobal(KWBlockLayout *block); + +FOUNDATION_EXPORT +BOOL KWBlockLayoutHasStructureReturn(KWBlockLayout *block); + +FOUNDATION_EXPORT +BOOL KWBlockLayoutHasSignature(KWBlockLayout *block); + +FOUNDATION_EXPORT +BOOL KWBlockLayoutHasExtendedLayout(KWBlockLayout *block); + +FOUNDATION_EXPORT +IMP KWBlockLayoutGetImp(KWBlockLayout *block); + +FOUNDATION_EXPORT +uintptr_t KWBlockLayoutGetDescriptorSize(KWBlockLayout *block); + +FOUNDATION_EXPORT +const char *KWBlockLayoutGetSignature(KWBlockLayout *block); + +FOUNDATION_EXPORT +NSMethodSignature *KWBlockLayoutGetMethodSignature(KWBlockLayout *block); + +FOUNDATION_EXPORT +KWBlockDescriptor *KWBlockLayoutGetDescriptor(KWBlockLayout *block); + +FOUNDATION_EXPORT +KWBlockDescriptorMetadata *KWBlockLayoutGetDescriptorMetadata(KWBlockLayout *block); + +FOUNDATION_EXPORT +IMP KWBlockLayoutGetForwardingImp(KWBlockLayout *block); diff --git a/Classes/Core/KWBlockLayout.m b/Classes/Core/KWBlockLayout.m new file mode 100644 index 00000000..a440ac89 --- /dev/null +++ b/Classes/Core/KWBlockLayout.m @@ -0,0 +1,108 @@ +// +// KWBlockLayout.m +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/16/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import + +#import "KWBlockLayout.h" +#import "KWBlockSignature.h" + +kKWBlockOptions KWBlockLayoutGetFlags(KWBlockLayout *block) { + // refcount is unneeded, as wel, as deallocating, so we filter them out from the flags + return block->flags & ~(kKWBlockRefcountMask | kKWBlockDeallocating); +} + +BOOL KWBlockLayoutGetOption(KWBlockLayout *block, kKWBlockOptions option) { + return (KWBlockLayoutGetFlags(block) & option) > 0; +} + +BOOL KWBlockLayoutHasCopyDispose(KWBlockLayout *block) { + return KWBlockLayoutGetOption(block, kKWBlockHasCopyDispose); +} + +BOOL KWBlockLayoutHasCTOR(KWBlockLayout *block) { + return KWBlockLayoutGetOption(block, kKWBlockHasCTOR); +} + +BOOL KWBlockLayoutIsGlobal(KWBlockLayout *block) { + return KWBlockLayoutGetOption(block, kKWBlockIsGlobal); +} + +BOOL KWBlockLayoutHasStructureReturn(KWBlockLayout *block) { + // block only has structure return, when it has signature + return KWBlockLayoutGetOption(block, kKWBlockHasStructureReturn) && KWBlockLayoutHasSignature(block); +} + +BOOL KWBlockLayoutHasSignature(KWBlockLayout *block) { + return KWBlockLayoutGetOption(block, kKWBlockHasSignature); +} + +BOOL KWBlockLayoutHasExtendedLayout(KWBlockLayout *block) { + return KWBlockLayoutGetOption(block, kKWBlockHasExtendedLayout); +} + +IMP KWBlockLayoutGetImp(KWBlockLayout *block) { + return block->imp; +} + +uintptr_t KWBlockLayoutGetDescriptorSize(KWBlockLayout *block) { + return KWBlockLayoutGetDescriptor(block)->size; +} + +const char *KWBlockLayoutGetSignature(KWBlockLayout *block) { + if (!KWBlockLayoutHasSignature(block)) { + return NULL; + } + + return KWBlockLayoutGetDescriptorMetadata(block)->signature; +} + +NSMethodSignature *KWBlockLayoutGetMethodSignature(KWBlockLayout *block) { + // if there is no signature, we consider block to be returning void + const char *UTF8Signature = KWBlockLayoutHasSignature(block) ? KWBlockLayoutGetSignature(block) : @encode(void); + + NSString *signature = [NSString stringWithFormat: @"%s", UTF8Signature]; + NSMethodSignature *result = [KWBlockSignature signatureWithObjCTypes:signature.UTF8String]; + + // If there are not enough arguments, we append them by adding void *, which is valid for both id and SEL + // Forwarding expects the ObjC signature return(self, _cmd) + if (result.numberOfArguments < 1) { + signature = [NSString stringWithFormat:@"%@%s", signature, @encode(void *)]; + result = [NSMethodSignature signatureWithObjCTypes:signature.UTF8String]; + } + + return result; +} + +KWBlockDescriptor *KWBlockLayoutGetDescriptor(KWBlockLayout *block) { + return block->descriptor; +} + +KWBlockDescriptorMetadata *KWBlockLayoutGetDescriptorMetadata(KWBlockLayout *block) { + // signature is only available, if the appropriate flag is set + if (!KWBlockLayoutHasSignature(block)) { + return NULL; + } + + // _Block_descriptor_3: http://opensource.apple.com//source/libclosure/libclosure-65/runtime.c + // It's layed out from descriptor pointer inside KWBlockLayout in the following way: + // - KWBlockDescriptor + // - KWBlockDescriptorCopyDispose - if kKWBlockHasCopyDispose flag is set, otherwise it's not there + // - KWBlockDescriptorMetadata + uint8_t *address = (uint8_t *)KWBlockLayoutGetDescriptor(block); + address += sizeof(KWBlockDescriptor); + if (KWBlockLayoutHasCopyDispose(block)) { + address += sizeof(KWBlockDescriptorCopyDispose); + } + + return (KWBlockDescriptorMetadata *)address; +} + +IMP KWBlockLayoutGetForwardingImp(KWBlockLayout *block) { + // explicit type casting for OBJC_OLD_DISPATCH_PROTOTYPES + return (IMP)(KWBlockLayoutHasStructureReturn(block) ? _objc_msgForward_stret : _objc_msgForward); +} diff --git a/Classes/Core/KWBlockMessagePattern.h b/Classes/Core/KWBlockMessagePattern.h new file mode 100644 index 00000000..d53bb54a --- /dev/null +++ b/Classes/Core/KWBlockMessagePattern.h @@ -0,0 +1,31 @@ +// +// KWMessagePattern.h +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/19/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import "KWMessagePattern.h" + +@interface KWBlockMessagePattern : KWMessagePattern + +#pragma mark - Initializing + +- (id)initWithSignature:(NSMethodSignature *)signature; +- (id)initWithSignature:(NSMethodSignature *)signature argumentFilters:(NSArray *)anArray; +- (id)initWithSignature:(NSMethodSignature *)signature + firstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList; + ++ (id)messagePatternWithSignature:(NSMethodSignature *)signature; ++ (id)messagePatternWithSignature:(NSMethodSignature *)signature argumentFilters:(NSArray *)anArray; ++ (id)messagePatternWithSignature:(NSMethodSignature *)signature + firstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList; + +#pragma mark - Properties + +@property (nonatomic, readonly) NSMethodSignature *signature; + +@end diff --git a/Classes/Core/KWBlockMessagePattern.m b/Classes/Core/KWBlockMessagePattern.m new file mode 100644 index 00000000..0de53b92 --- /dev/null +++ b/Classes/Core/KWBlockMessagePattern.m @@ -0,0 +1,109 @@ +// +// KWBlockMessagePattern.m +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/19/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import "KWBlockMessagePattern.h" + +#import "KWFormatter.h" +#import "NSMethodSignature+KiwiAdditions.h" + +@implementation KWBlockMessagePattern + +#pragma mark - Initializing + +- (id)initWithSignature:(NSMethodSignature *)signature { + return [self initWithSignature:signature argumentFilters:nil]; +} + +- (id)initWithSignature:(NSMethodSignature *)signature argumentFilters:(NSArray *)anArray { + self = [super initWithArgumentFilters:anArray]; + if (self) { + _signature = signature; + } + + return self; +} + +- (id)initWithSignature:(NSMethodSignature *)signature + firstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList +{ + NSUInteger argumentCount = [signature numberOfMessageArguments]; + NSArray *argumentFilters = [self argumentFiltersWithFirstArgumentFilter:firstArgumentFilter + argumentList:argumentList + argumentCount:argumentCount]; + + return [self initWithSignature:signature argumentFilters:argumentFilters]; +} + +- (id)initWithInvocation:(NSInvocation *)anInvocation { + NSArray *argumentFilters = [self argumentFiltersWithInvocation:anInvocation]; + + return [self initWithSignature:anInvocation.methodSignature argumentFilters:argumentFilters]; +} + ++ (id)messagePatternWithSignature:(NSMethodSignature *)signature { + return [[self alloc] initWithSignature:signature]; +} + ++ (id)messagePatternWithSignature:(NSMethodSignature *)signature argumentFilters:(NSArray *)anArray { + return [[self alloc] initWithSignature:signature argumentFilters:anArray]; +} + ++ (id)messagePatternWithSignature:(NSMethodSignature *)signature + firstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList +{ + return [[self alloc] initWithSignature:signature firstArgumentFilter:firstArgumentFilter argumentList:argumentList]; +} + +#pragma mark - Properties + +- (SEL)selector { + return NULL; +} + +#pragma mark - Comparing Message Patterns + +- (NSUInteger)hash { + return [super hash] ^ [self.signature hash]; +} + +#pragma mark - Retrieving String Representations + + +- (NSString *)stringValue { + NSMutableString *description = [NSMutableString stringWithString:@"block call( "]; + NSArray *argumentFilters = self.argumentFilters; + + NSUInteger count = [argumentFilters count]; + + for (NSUInteger i = 0; i < count; ++i) { + NSString *argumentFilterString = [KWFormatter formatObject:(self.argumentFilters)[i]]; + [description appendFormat:@"%@, ", argumentFilterString]; + } + + [description appendFormat:@")"]; + + return description; +} + +#pragma mark - Debugging + +- (NSString *)description { + return [NSString stringWithFormat:@"argumentFilters: %@", self.argumentFilters]; +} + +#pragma mark - Invocation Handling + +- (NSUInteger)argumentCountWithInvocation:(NSInvocation *)anInvocation { + NSMethodSignature *signature = [anInvocation methodSignature]; + + return [signature numberOfMessageArguments]; +} + +@end diff --git a/Classes/Core/KWBlockSignature.h b/Classes/Core/KWBlockSignature.h new file mode 100644 index 00000000..dee3f344 --- /dev/null +++ b/Classes/Core/KWBlockSignature.h @@ -0,0 +1,13 @@ +// +// KWBlockSignature.h +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/27/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import + +@interface KWBlockSignature : NSMethodSignature + +@end diff --git a/Classes/Core/KWBlockSignature.m b/Classes/Core/KWBlockSignature.m new file mode 100644 index 00000000..b55ba0df --- /dev/null +++ b/Classes/Core/KWBlockSignature.m @@ -0,0 +1,24 @@ +// +// KWBlockSignature.m +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/27/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import "KWBlockSignature.h" + +@implementation KWBlockSignature + +#pragma mark - Getting Information on Message Arguments + +- (NSUInteger)numberOfMessageArguments { + return [self numberOfArguments] - 1; +} + +- (const char *)messageArgumentTypeAtIndex:(NSUInteger)anIndex { + return [self getArgumentTypeAtIndex:anIndex + 1]; +} + + +@end diff --git a/Classes/Core/KWMessagePattern.h b/Classes/Core/KWMessagePattern.h index 81449135..18f22014 100644 --- a/Classes/Core/KWMessagePattern.h +++ b/Classes/Core/KWMessagePattern.h @@ -1,7 +1,9 @@ // -// Licensed under the terms in License.txt +// KWAbstractMessagePattern.h +// Kiwi // -// Copyright 2010 Allen Ding. All rights reserved. +// Created by Oleksa 'trimm' Korin on 4/26/16. +// Copyright © 2016 Allen Ding. All rights reserved. // #import "KiwiConfiguration.h" @@ -10,19 +12,13 @@ #pragma mark - Initializing -- (id)initWithSelector:(SEL)aSelector; -- (id)initWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray; -- (id)initWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList; - -+ (id)messagePatternWithSelector:(SEL)aSelector; -+ (id)messagePatternWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray; -+ (id)messagePatternWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList; - + (id)messagePatternFromInvocation:(NSInvocation *)anInvocation; +// this method should be overloaded by subclasses +- (id)initWithInvocation:(NSInvocation *)anInvocation; + #pragma mark - Properties -@property (nonatomic, readonly) SEL selector; @property (nonatomic, readonly) NSArray *argumentFilters; #pragma mark - Matching Invocations @@ -31,10 +27,69 @@ #pragma mark - Comparing Message Patterns -- (BOOL)isEqualToMessagePattern:(KWMessagePattern *)aMessagePattern; +- (BOOL)isEqualToMessagePattern:(id)aMessagePattern; #pragma mark - Retrieving String Representations +// this method should be overloaded by subclasses - (NSString *)stringValue; @end + +// cluster methods for KWSelectorMessagePattern +@interface KWMessagePattern (KWSelectorMessagePatternCluster) + +#pragma mark - Initializing + +- (id)initWithSelector:(SEL)aSelector NS_REPLACES_RECEIVER; +- (id)initWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray NS_REPLACES_RECEIVER; +- (id)initWithSelector:(SEL)aSelector + firstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList NS_REPLACES_RECEIVER; + ++ (id)messagePatternWithSelector:(SEL)aSelector; ++ (id)messagePatternWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray; ++ (id)messagePatternWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList; + +@end + +// cluster methods for KWBlockMessagePattern +@interface KWMessagePattern (KWBlockMessagePatternCluster) + +#pragma mark - Initializing + +- (id)initWithSignature:(NSMethodSignature *)signature NS_REPLACES_RECEIVER; +- (id)initWithSignature:(NSMethodSignature *)signature argumentFilters:(NSArray *)anArray NS_REPLACES_RECEIVER; +- (id)initWithSignature:(NSMethodSignature *)signature + firstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList NS_REPLACES_RECEIVER; + ++ (id)messagePatternWithSignature:(NSMethodSignature *)signature; ++ (id)messagePatternWithSignature:(NSMethodSignature *)signature argumentFilters:(NSArray *)anArray; ++ (id)messagePatternWithSignature:(NSMethodSignature *)signature + firstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList; + +@end + +// methods in this category are used for inheritance and should not be called directly +@interface KWMessagePattern (KWMessagePatternPrivate) + +#pragma mark - Initializing + +- (id)initWithArgumentFilters:(NSArray *)anArray; + +#pragma mark - Argument Filters Creation + +- (NSArray *)argumentFiltersWithInvocation:(NSInvocation *)invocation; +- (NSArray *)argumentFiltersWithFirstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList + argumentCount:(NSUInteger)count; + +#pragma mark - Invocation Handling + +// this method should be overloaded by subclasses +- (NSUInteger)argumentCountWithInvocation:(NSInvocation *)invocation; + +@end + diff --git a/Classes/Core/KWMessagePattern.m b/Classes/Core/KWMessagePattern.m index 0e4bbc4f..317cd74b 100644 --- a/Classes/Core/KWMessagePattern.m +++ b/Classes/Core/KWMessagePattern.m @@ -1,118 +1,67 @@ // -// Licensed under the terms in License.txt +// KWAbstractMessagePattern.m +// Kiwi // -// Copyright 2010 Allen Ding. All rights reserved. +// Created by Oleksa 'trimm' Korin on 4/26/16. +// Copyright © 2016 Allen Ding. All rights reserved. // #import "KWMessagePattern.h" -#import "KWAny.h" -#import "KWFormatter.h" + #import "KWNull.h" -#import "KWObjCUtilities.h" -#import "KWValue.h" #import "NSInvocation+KiwiAdditions.h" #import "NSMethodSignature+KiwiAdditions.h" +#import "KWObjCUtilities.h" +#import "KWAny.h" +#import "KWValue.h" +#import "KWFormatter.h" #import "KWGenericMatchEvaluator.h" +#import "KWBlockMessagePattern.h" +#import "KWSelectorMessagePattern.h" + @implementation KWMessagePattern #pragma mark - Initializing -- (id)initWithSelector:(SEL)aSelector { - return [self initWithSelector:aSelector argumentFilters:nil]; -} - -- (id)initWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray { +- (id)initWithArgumentFilters:(NSArray *)anArray { self = [super init]; if (self) { - selector = aSelector; - if ([anArray count] > 0) - argumentFilters = [anArray copy]; + _argumentFilters = [anArray copy]; } - + return self; } -- (id)initWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList { - NSUInteger count = KWSelectorParameterCount(aSelector); - NSMutableArray *array = [NSMutableArray arrayWithCapacity:count]; - [array addObject:(firstArgumentFilter != nil) ? firstArgumentFilter : [KWNull null]]; - - for (NSUInteger i = 1; i < count; ++i) - { - id object = va_arg(argumentList, id); - [array addObject:(object != nil) ? object : [KWNull null]]; - } - - va_end(argumentList); - return [self initWithSelector:aSelector argumentFilters:array]; -} - -+ (id)messagePatternWithSelector:(SEL)aSelector { - return [self messagePatternWithSelector:aSelector argumentFilters:nil]; -} - -+ (id)messagePatternWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray { - return [[self alloc] initWithSelector:aSelector argumentFilters:anArray]; -} - -+ (id)messagePatternWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList { - return [[self alloc] initWithSelector:aSelector firstArgumentFilter:firstArgumentFilter argumentList:argumentList]; +- (id)initWithInvocation:(NSInvocation *)anInvocation { + return [self initWithArgumentFilters:[self argumentFiltersWithInvocation:anInvocation]]; } + (id)messagePatternFromInvocation:(NSInvocation *)anInvocation { - NSMethodSignature *signature = [anInvocation methodSignature]; - NSUInteger numberOfMessageArguments = [signature numberOfMessageArguments]; - NSMutableArray *argumentFilters = nil; - - if (numberOfMessageArguments > 0) { - argumentFilters = [[NSMutableArray alloc] initWithCapacity:numberOfMessageArguments]; - - for (NSUInteger i = 0; i < numberOfMessageArguments; ++i) { - const char *type = [signature messageArgumentTypeAtIndex:i]; - void* argumentDataBuffer = malloc(KWObjCTypeLength(type)); - [anInvocation getMessageArgument:argumentDataBuffer atIndex:i]; - id object = nil; - if(*(__unsafe_unretained id*)argumentDataBuffer != [KWAny any] && !KWObjCTypeIsObject(type)) { - NSData *data = [anInvocation messageArgumentDataAtIndex:i]; - object = [KWValue valueWithBytes:[data bytes] objCType:type]; - } else { - object = *(__unsafe_unretained id*)argumentDataBuffer; - - if (object != [KWAny any] && KWObjCTypeIsBlock(type)) { - object = [object copy]; // Converting NSStackBlock to NSMallocBlock - } - } - - [argumentFilters addObject:(object != nil) ? object : [KWNull null]]; - - free(argumentDataBuffer); - } - } - - return [self messagePatternWithSelector:[anInvocation selector] argumentFilters:argumentFilters]; + return [[self alloc] initWithInvocation:anInvocation]; } #pragma mark - Properties -@synthesize selector; -@synthesize argumentFilters; +- (NSUInteger)argumentCount { + return 0; +} #pragma mark - Matching Invocations - (BOOL)argumentFiltersMatchInvocationArguments:(NSInvocation *)anInvocation { if (self.argumentFilters == nil) return YES; - + NSMethodSignature *signature = [anInvocation methodSignature]; NSUInteger numberOfArgumentFilters = [self.argumentFilters count]; - NSUInteger numberOfMessageArguments = [signature numberOfMessageArguments]; - + NSUInteger numberOfMessageArguments = [self argumentCountWithInvocation:anInvocation]; + for (NSUInteger i = 0; i < numberOfMessageArguments && i < numberOfArgumentFilters; ++i) { const char *objCType = [signature messageArgumentTypeAtIndex:i]; id __autoreleasing object = nil; - + // Extract message argument into object (wrapping values if neccesary) if (KWObjCTypeIsObject(objCType) || KWObjCTypeIsClass(objCType)) { [anInvocation getMessageArgument:&object atIndex:i]; @@ -120,14 +69,14 @@ - (BOOL)argumentFiltersMatchInvocationArguments:(NSInvocation *)anInvocation { NSData *data = [anInvocation messageArgumentDataAtIndex:i]; object = [KWValue valueWithBytes:[data bytes] objCType:objCType]; } - + // Match argument filter to object id argumentFilter = (self.argumentFilters)[i]; - + if ([argumentFilter isEqual:[KWAny any]]) { continue; } - + if ([KWGenericMatchEvaluator isGenericMatcher:argumentFilter]) { id matcher = argumentFilter; if ([object isKindOfClass:[KWValue class]] && [object isNumeric]) { @@ -150,70 +99,183 @@ - (BOOL)argumentFiltersMatchInvocationArguments:(NSInvocation *)anInvocation { return NO; } } - + return YES; } - (BOOL)matchesInvocation:(NSInvocation *)anInvocation { - return self.selector == [anInvocation selector] && [self argumentFiltersMatchInvocationArguments:anInvocation]; + return [self argumentFiltersMatchInvocationArguments:anInvocation]; } #pragma mark - Comparing Message Patterns - (NSUInteger)hash { - return [NSStringFromSelector(self.selector) hash]; + return [self.argumentFilters hash];; } - (BOOL)isEqual:(id)object { - if (![object isKindOfClass:[KWMessagePattern class]]) + if (![object isKindOfClass:[self class]]) return NO; - + return [self isEqualToMessagePattern:object]; } - (BOOL)isEqualToMessagePattern:(KWMessagePattern *)aMessagePattern { - if (self.selector != aMessagePattern.selector) - return NO; - if (self.argumentFilters == nil && aMessagePattern.argumentFilters == nil) return YES; - + return [self.argumentFilters isEqualToArray:aMessagePattern.argumentFilters]; } #pragma mark - Retrieving String Representations -- (NSString *)selectorString { - return NSStringFromSelector(self.selector); +- (NSString *)stringValue { + return nil; +} + +#pragma mark - Debugging + +- (NSString *)description { + return [NSString stringWithFormat:@"argumentFilters: %@", self.argumentFilters]; } -- (NSString *)selectorAndArgumentFiltersString { - NSMutableString *description = [[NSMutableString alloc] init]; - NSArray *components = [NSStringFromSelector(self.selector) componentsSeparatedByString:@":"]; - NSUInteger count = [components count] - 1; +#pragma mark - Argument Filters Creation - for (NSUInteger i = 0; i < count; ++i) { - NSString *selectorComponent = components[i]; - NSString *argumentFilterString = [KWFormatter formatObject:(self.argumentFilters)[i]]; - [description appendFormat:@"%@:%@ ", selectorComponent, argumentFilterString]; +- (NSArray *)argumentFiltersWithInvocation:(NSInvocation *)anInvocation { + NSMethodSignature *signature = [anInvocation methodSignature]; + NSUInteger numberOfMessageArguments = [self argumentCountWithInvocation:anInvocation]; + NSMutableArray *argumentFilters = nil; + + if (numberOfMessageArguments > 0) { + argumentFilters = [[NSMutableArray alloc] initWithCapacity:numberOfMessageArguments]; + + for (NSUInteger i = 0; i < numberOfMessageArguments; ++i) { + const char *type = [signature messageArgumentTypeAtIndex:i]; + void* argumentDataBuffer = malloc(KWObjCTypeLength(type)); + [anInvocation getMessageArgument:argumentDataBuffer atIndex:i]; + id object = nil; + if(*(__unsafe_unretained id*)argumentDataBuffer != [KWAny any] && !KWObjCTypeIsObject(type)) { + NSData *data = [anInvocation messageArgumentDataAtIndex:i]; + object = [KWValue valueWithBytes:[data bytes] objCType:type]; + } else { + object = *(__unsafe_unretained id*)argumentDataBuffer; + + if (object != [KWAny any] && KWObjCTypeIsBlock(type)) { + object = [object copy]; // Converting NSStackBlock to NSMallocBlock + } + } + + [argumentFilters addObject:(object != nil) ? object : [KWNull null]]; + + free(argumentDataBuffer); + } } + + return argumentFilters; +} - return description; +- (NSArray *)argumentFiltersWithFirstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList + argumentCount:(NSUInteger)count +{ + NSMutableArray *array = [NSMutableArray arrayWithCapacity:count]; + [array addObject:(firstArgumentFilter != nil) ? firstArgumentFilter : [KWNull null]]; + + for (NSUInteger i = 1; i < count; ++i) { + id object = va_arg(argumentList, id); + [array addObject:(object != nil) ? object : [KWNull null]]; + } + + va_end(argumentList); + + return array; } -- (NSString *)stringValue { - if (self.argumentFilters == nil) - return [self selectorString]; - else - return [self selectorAndArgumentFiltersString]; +#pragma mark - Invocation Handling + +- (NSUInteger)argumentCountWithInvocation:(NSInvocation *)invocation { + return 0; } -#pragma mark - Debugging +@end -- (NSString *)description { - return [NSString stringWithFormat:@"selector: %@\nargumentFilters: %@", - NSStringFromSelector(self.selector), - self.argumentFilters]; +// cluster methods for KWSelectorMessagePattern +@implementation KWMessagePattern (KWSelectorMessagePatternCluster) + +#pragma mark - Initializing + +- (id)initWithSelector:(SEL)aSelector { + return [[KWSelectorMessagePattern alloc] initWithSelector:aSelector]; +} + +- (id)initWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray { + return [[KWSelectorMessagePattern alloc] initWithSelector:aSelector argumentFilters:anArray]; +} + +- (id)initWithSelector:(SEL)aSelector + firstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList +{ + return [[KWSelectorMessagePattern alloc] initWithSelector:aSelector + firstArgumentFilter:firstArgumentFilter + argumentList:argumentList]; +} + ++ (id)messagePatternWithSelector:(SEL)aSelector { + return [KWSelectorMessagePattern messagePatternWithSelector:aSelector]; +} + ++ (id)messagePatternWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray { + return [KWSelectorMessagePattern messagePatternWithSelector:aSelector argumentFilters:anArray]; +} + ++ (id)messagePatternWithSelector:(SEL)aSelector + firstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList +{ + return [KWSelectorMessagePattern messagePatternWithSelector:aSelector + firstArgumentFilter:firstArgumentFilter + argumentList:argumentList]; +} + +@end + +// cluster methods for KWBlockMessagePattern +@implementation KWMessagePattern (KWBlockMessagePatternCluster) + +#pragma mark - Initializing + +- (id)initWithSignature:(NSMethodSignature *)signature { + return [[KWBlockMessagePattern alloc] initWithSignature:signature]; +} + +- (id)initWithSignature:(NSMethodSignature *)signature argumentFilters:(NSArray *)anArray { + return [[KWBlockMessagePattern alloc] initWithSignature:signature argumentFilters:anArray]; +} +- (id)initWithSignature:(NSMethodSignature *)signature + firstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList +{ + return [[KWBlockMessagePattern alloc] initWithSignature:signature + firstArgumentFilter:firstArgumentFilter + argumentList:argumentList]; +} + ++ (id)messagePatternWithSignature:(NSMethodSignature *)signature { + return [KWBlockMessagePattern messagePatternWithSignature:signature]; +} + ++ (id)messagePatternWithSignature:(NSMethodSignature *)signature argumentFilters:(NSArray *)anArray { + return [KWBlockMessagePattern messagePatternWithSignature:signature argumentFilters:anArray]; +} + ++ (id)messagePatternWithSignature:(NSMethodSignature *)signature + firstArgumentFilter:(id)firstArgumentFilter + argumentList:(va_list)argumentList +{ + return [KWBlockMessagePattern messagePatternWithSignature:signature + firstArgumentFilter:firstArgumentFilter + argumentList:argumentList]; } @end diff --git a/Classes/Core/KWProxyBlock.h b/Classes/Core/KWProxyBlock.h new file mode 100644 index 00000000..c39d3071 --- /dev/null +++ b/Classes/Core/KWProxyBlock.h @@ -0,0 +1,31 @@ +// +// KWProxyBlock.h +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/18/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import + +@interface KWProxyBlock : NSObject + +#pragma mark - Initializing + ++ (id)blockWithBlock:(id)block; + +- (id)initWithBlock:(id)block; + +#pragma mark - Properties + +@property (nonatomic, readonly) NSMethodSignature *methodSignature; + +@end + +#pragma mark - Creating Blocks + +FOUNDATION_EXPORT +KWProxyBlock *theBlockProxy(id); + +FOUNDATION_EXPORT +KWProxyBlock *lambdaProxy(id); diff --git a/Classes/Core/KWProxyBlock.m b/Classes/Core/KWProxyBlock.m new file mode 100644 index 00000000..d5cda9a8 --- /dev/null +++ b/Classes/Core/KWProxyBlock.m @@ -0,0 +1,177 @@ +// +// KWProxyBlock.m +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/18/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import "KWProxyBlock.h" + +#import "KWBlockLayout.h" +#import "KWBlockMessagePattern.h" +#import "KWMessageSpying.h" + +#import "NSObject+KiwiStubAdditions.h" +#import "NSInvocation+KiwiAdditions.h" + +@interface NSInvocation (KWPrivateInterface) + +- (void)invokeUsingIMP:(IMP)imp; + +@end + +@interface KWProxyBlock() + +#pragma mark - Properties + +@property (nonatomic, readonly, copy) id block; +@property (nonatomic, readonly, assign) KWBlockLayout *blockLayout; +@property (nonatomic, readonly, assign) KWBlockDescriptor *descriptor; + +@property (nonatomic, readonly) NSMutableSet *expectedMessagePatterns; +@property (nonatomic, readonly) NSMapTable *messageSpies; + +#pragma mark - Methods + +- (void)interposeBlock:(id)block; + +@end + +@implementation KWProxyBlock { + // we imitate the block layout for block forwarding to work + // the order of ivars is important + volatile int32_t _flags; + int32_t _reserved; + IMP _imp; + KWBlockDescriptor *_descriptor; + + // ivars related to our class, rather, than to the interposed block + id _block; +} + +@synthesize block = _block; +@synthesize descriptor = _descriptor; + +@dynamic blockLayout; +@dynamic methodSignature; + +#pragma mark - Deallocating + +- (void)dealloc { + free(_descriptor); + _descriptor = NULL; +} + +#pragma mark - Initializing + +- (id)initWithBlock:(id)block { + self = [super init]; + if (self) { + _expectedMessagePatterns = [NSMutableSet new]; + _messageSpies = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory]; + + _block = [block copy]; + [self interposeBlock:_block]; + } + + return self; +} + ++ (id)blockWithBlock:(id)aBlock { + return [[self alloc] initWithBlock:aBlock]; +} + +#pragma mark - Block Interposing + +- (void)interposeBlock:(id)interposeBlock { + KWBlockLayout *block = (__bridge KWBlockLayout *)interposeBlock; + + _flags = KWBlockLayoutGetFlags(block); + + if (_descriptor) { + free(_descriptor); + } + + uintptr_t interposeSize = KWBlockLayoutGetDescriptorSize(block); + _descriptor = calloc(1, interposeSize); + + KWBlockDescriptor *descriptor = KWBlockLayoutGetDescriptor(block); + memcpy(_descriptor, descriptor, interposeSize); + + _imp = KWBlockLayoutGetForwardingImp(block); +} + +- (id)copyWithZone:(nullable NSZone *)zone { + return self; +} + +#pragma mark - Properties + +- (KWBlockLayout *)blockLayout { + return (__bridge KWBlockLayout *)(self.block); +} + +- (NSMethodSignature *)methodSignature { + return KWBlockLayoutGetMethodSignature(self.blockLayout); +} + +#pragma mark - Forwarding + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { + return self.methodSignature; +} + +- (void)forwardInvocation:(NSInvocation *)anInvocation { + [anInvocation becomeBlockInvocation]; + + NSMapTable *spiesMap = self.messageSpies; + for (KWBlockMessagePattern *messagePattern in spiesMap) { + if ([messagePattern matchesInvocation:anInvocation]) { + NSArray *spies = [spiesMap objectForKey:messagePattern]; + + for (id spy in spies) { + [spy object:self didReceiveInvocation:anInvocation]; + } + } + } + + [anInvocation setTarget:self.block]; + [anInvocation invokeUsingIMP:KWBlockLayoutGetImp(self.blockLayout)]; +} + +- (void)addMessageSpy:(id)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern { + if (![aMessagePattern isKindOfClass:[KWBlockMessagePattern class]]) { + [super addMessageSpy:aSpy forMessagePattern:aMessagePattern]; + } + + [self.expectedMessagePatterns addObject:aMessagePattern]; + NSMutableArray *messagePatternSpies = [self.messageSpies objectForKey:aMessagePattern]; + + if (messagePatternSpies == nil) { + messagePatternSpies = [[NSMutableArray alloc] init]; + [self.messageSpies setObject:messagePatternSpies forKey:aMessagePattern]; + } + + if (![messagePatternSpies containsObject:aSpy]) + [messagePatternSpies addObject:aSpy]; +} + +- (void)removeMessageSpy:(id)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern { + if (![aMessagePattern isKindOfClass:[KWBlockMessagePattern class]]) { + [super removeMessageSpy:aSpy forMessagePattern:aMessagePattern]; + } + + NSMutableArray *messagePatternSpies = [self.messageSpies objectForKey:aMessagePattern]; + [messagePatternSpies removeObject:aSpy]; +} + +@end + +KWProxyBlock *theBlockProxy(id block) { + return [KWProxyBlock blockWithBlock:block]; +} + +KWProxyBlock *lambdaProxy(id block) { + return theBlockProxy(block); +} diff --git a/Classes/Core/KWSelectorMessagePattern.h b/Classes/Core/KWSelectorMessagePattern.h new file mode 100644 index 00000000..d274eceb --- /dev/null +++ b/Classes/Core/KWSelectorMessagePattern.h @@ -0,0 +1,25 @@ +// +// Licensed under the terms in License.txt +// +// Copyright 2010 Allen Ding. All rights reserved. +// + +#import "KWMessagePattern.h" + +@interface KWSelectorMessagePattern : KWMessagePattern + +#pragma mark - Initializing + +- (id)initWithSelector:(SEL)aSelector; +- (id)initWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray; +- (id)initWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList; + ++ (id)messagePatternWithSelector:(SEL)aSelector; ++ (id)messagePatternWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray; ++ (id)messagePatternWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList; + +#pragma mark - Properties + +@property (nonatomic, readonly) SEL selector; + +@end diff --git a/Classes/Core/KWSelectorMessagePattern.m b/Classes/Core/KWSelectorMessagePattern.m new file mode 100644 index 00000000..ff5843dc --- /dev/null +++ b/Classes/Core/KWSelectorMessagePattern.m @@ -0,0 +1,124 @@ +// +// Licensed under the terms in License.txt +// +// Copyright 2010 Allen Ding. All rights reserved. +// + +#import "KWSelectorMessagePattern.h" +#import "KWAny.h" +#import "KWFormatter.h" +#import "KWNull.h" +#import "KWObjCUtilities.h" +#import "KWValue.h" +#import "NSInvocation+KiwiAdditions.h" +#import "NSMethodSignature+KiwiAdditions.h" +#import "KWGenericMatchEvaluator.h" + +@implementation KWSelectorMessagePattern + +#pragma mark - Initializing + +- (id)initWithSelector:(SEL)aSelector { + return [self initWithSelector:aSelector argumentFilters:nil]; +} + +- (id)initWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray { + self = [super initWithArgumentFilters:anArray];; + if (self) { + _selector = aSelector; + } + + return self; +} + +- (id)initWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList { + NSUInteger count = KWSelectorParameterCount(aSelector); + + NSArray *argumentFilters = [self argumentFiltersWithFirstArgumentFilter:firstArgumentFilter + argumentList:argumentList + argumentCount:count]; + + return [self initWithSelector:aSelector argumentFilters:argumentFilters]; +} + +- (id)initWithInvocation:(NSInvocation *)anInvocation { + NSArray *argumentFilters = [self argumentFiltersWithInvocation:anInvocation]; + + return [self initWithSelector:anInvocation.selector argumentFilters:argumentFilters]; +} + ++ (id)messagePatternWithSelector:(SEL)aSelector { + return [self messagePatternWithSelector:aSelector argumentFilters:nil]; +} + ++ (id)messagePatternWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray { + return [[self alloc] initWithSelector:aSelector argumentFilters:anArray]; +} + ++ (id)messagePatternWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList { + return [[self alloc] initWithSelector:aSelector firstArgumentFilter:firstArgumentFilter argumentList:argumentList]; +} + +#pragma mark - Matching Invocations + +- (BOOL)matchesInvocation:(NSInvocation *)anInvocation { + return self.selector == [anInvocation selector] && [super matchesInvocation:anInvocation]; +} + +#pragma mark - Comparing Message Patterns + +- (NSUInteger)hash { + return [NSStringFromSelector(self.selector) hash]; +} + +- (BOOL)isEqualToMessagePattern:(KWSelectorMessagePattern *)aMessagePattern { + if (self.selector != aMessagePattern.selector) + return NO; + + return [super isEqualToMessagePattern:aMessagePattern]; +} + +#pragma mark - Retrieving String Representations + +- (NSString *)selectorString { + return NSStringFromSelector(self.selector); +} + +- (NSString *)selectorAndArgumentFiltersString { + NSMutableString *description = [[NSMutableString alloc] init]; + NSArray *components = [NSStringFromSelector(self.selector) componentsSeparatedByString:@":"]; + NSUInteger count = [components count] - 1; + + for (NSUInteger i = 0; i < count; ++i) { + NSString *selectorComponent = components[i]; + NSString *argumentFilterString = [KWFormatter formatObject:(self.argumentFilters)[i]]; + [description appendFormat:@"%@:%@ ", selectorComponent, argumentFilterString]; + } + + return description; +} + +- (NSString *)stringValue { + if (self.argumentFilters == nil) + return [self selectorString]; + else + return [self selectorAndArgumentFiltersString]; +} + +#pragma mark - Debugging + +- (NSString *)description { + return [NSString stringWithFormat:@"selector: %@\nargumentFilters: %@", + NSStringFromSelector(self.selector), + self.argumentFilters]; +} + +#pragma mark - Invocation Handling + +- (NSUInteger)argumentCountWithInvocation:(NSInvocation *)anInvocation { + NSMethodSignature *signature = [anInvocation methodSignature]; + + return [signature numberOfMessageArguments]; +} + +@end diff --git a/Classes/Core/Kiwi.h b/Classes/Core/Kiwi.h index a34bc2a9..527a4a56 100644 --- a/Classes/Core/Kiwi.h +++ b/Classes/Core/Kiwi.h @@ -27,6 +27,8 @@ extern "C" { #import #import #import +#import +#import #import #import #import @@ -47,6 +49,8 @@ extern "C" { #import #import #import +#import +#import #import #import #import @@ -61,12 +65,14 @@ extern "C" { #import #import #import +#import // Matchers #ifndef KIWI_DISABLE_MATCHERS #import #import +#import #import #import #import diff --git a/Classes/Core/NSInvocation+KiwiAdditions.h b/Classes/Core/NSInvocation+KiwiAdditions.h index d663b4c1..368fcbc7 100644 --- a/Classes/Core/NSInvocation+KiwiAdditions.h +++ b/Classes/Core/NSInvocation+KiwiAdditions.h @@ -13,6 +13,11 @@ + (NSInvocation *)invocationWithTarget:(id)anObject selector:(SEL)aSelector; + (NSInvocation *)invocationWithTarget:(id)anObject selector:(SEL)aSelector messageArguments:(const void *)firstBytes, ...; ++ (NSInvocation *)invocationWithTarget:(id)anObject + selector:(SEL)aSelector + firstArgument:(const void *)firstBytes + argumentList:(va_list)argumentList; + #pragma mark - Accessing Message Arguments // Message arguments are invocation arguments that begin after the target and selector arguments. These methods provide @@ -22,5 +27,14 @@ - (void)getMessageArgument:(void *)buffer atIndex:(NSUInteger)anIndex; - (void)setMessageArgument:(const void *)bytes atIndex:(NSUInteger)anIndex; - (void)setMessageArguments:(const void *)firstBytes, ...; +- (void)setMessageArgumentsWithFirstArgument:(const void *)firstBytes argumentList:(va_list)argumentList; + +#pragma mark - Argument Offset + +- (NSUInteger)argumentOffset; + +#pragma mark - Block Invocation + +- (void)becomeBlockInvocation; @end diff --git a/Classes/Core/NSInvocation+KiwiAdditions.m b/Classes/Core/NSInvocation+KiwiAdditions.m index 23065a2a..b6a35e86 100644 --- a/Classes/Core/NSInvocation+KiwiAdditions.m +++ b/Classes/Core/NSInvocation+KiwiAdditions.m @@ -5,10 +5,15 @@ // #import "NSInvocation+KiwiAdditions.h" + +#import + #import "KWFormatter.h" #import "KWObjCUtilities.h" #import "NSMethodSignature+KiwiAdditions.h" +#import "KWBlockInvocation.h" + @implementation NSInvocation(KiwiAdditions) #pragma mark - Creating NSInvocation Objects @@ -18,36 +23,38 @@ + (NSInvocation *)invocationWithTarget:(id)anObject selector:(SEL)aSelector { } + (NSInvocation *)invocationWithTarget:(id)anObject selector:(SEL)aSelector messageArguments:(const void *)firstBytes, ... { + va_list argumentList; + va_start(argumentList, firstBytes); + + return [self invocationWithTarget:anObject + selector:aSelector + firstArgument:firstBytes + argumentList:argumentList]; +} + ++ (NSInvocation *)invocationWithTarget:(id)anObject + selector:(SEL)aSelector + firstArgument:(const void *)firstBytes + argumentList:(va_list)argumentList +{ if (anObject == nil) { [NSException raise:NSInvalidArgumentException format:@"%@ - target must not be nil", - NSStringFromSelector(_cmd)]; + NSStringFromSelector(_cmd)]; } - + NSMethodSignature *signature = [anObject methodSignatureForSelector:aSelector]; - + if (signature == nil) { [NSException raise:NSInvalidArgumentException format:@"%@ - target returned nil for -methodSignatureForSelector", - NSStringFromSelector(_cmd)]; + NSStringFromSelector(_cmd)]; } - - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; + + NSInvocation *invocation = [self invocationWithMethodSignature:signature]; [invocation setTarget:anObject]; [invocation setSelector:aSelector]; - NSUInteger numberOfMessageArguments = [signature numberOfMessageArguments]; - - if (numberOfMessageArguments == 0) - return invocation; - - va_list argumentList; - va_start(argumentList, firstBytes); - const void *bytes = firstBytes; - - for (NSUInteger i = 0; i < numberOfMessageArguments && bytes != nil; ++i) { - [invocation setMessageArgument:bytes atIndex:i]; - bytes = va_arg(argumentList, const void *); - } - - va_end(argumentList); + + [invocation setMessageArgumentsWithFirstArgument:firstBytes argumentList:argumentList]; + return invocation; } @@ -63,29 +70,49 @@ - (NSData *)messageArgumentDataAtIndex:(NSUInteger)anIndex { } - (void)getMessageArgument:(void *)buffer atIndex:(NSUInteger)anIndex { - [self getArgument:buffer atIndex:anIndex + 2]; + [self getArgument:buffer atIndex:anIndex + self.argumentOffset]; } - (void)setMessageArgument:(const void *)bytes atIndex:(NSUInteger)anIndex { - [self setArgument:(void *)bytes atIndex:anIndex + 2]; + [self setArgument:(void *)bytes atIndex:anIndex + self.argumentOffset]; } - (void)setMessageArguments:(const void *)firstBytes, ... { - NSUInteger numberOfMessageArguments = [[self methodSignature] numberOfMessageArguments]; - - if (numberOfMessageArguments == 0) - return; - va_list argumentList; va_start(argumentList, firstBytes); - const void *bytes = firstBytes; + [self setMessageArgumentsWithFirstArgument:firstBytes argumentList:argumentList]; +} + +- (void)setMessageArgumentsWithFirstArgument:(const void *)firstBytes argumentList:(va_list)argumentList { + NSUInteger numberOfMessageArguments = [[self methodSignature] numberOfMessageArguments]; + + if (numberOfMessageArguments == 0) { + return; + va_end(argumentList); + } + + const void *bytes = firstBytes; + for (NSUInteger i = 0; i < numberOfMessageArguments && bytes != nil; ++i) { [self setMessageArgument:bytes atIndex:i]; bytes = va_arg(argumentList, const void *); } - + va_end(argumentList); } +#pragma mark - Argument Offset + +- (NSUInteger)argumentOffset { + return 2; +} + +#pragma mark - Block Invocation + +- (void)becomeBlockInvocation { + object_setClass(self, [KWBlockInvocation class]); +} + + @end diff --git a/Classes/Matchers/KWBeEvaluatedMatcher.h b/Classes/Matchers/KWBeEvaluatedMatcher.h new file mode 100644 index 00000000..2b2f4cef --- /dev/null +++ b/Classes/Matchers/KWBeEvaluatedMatcher.h @@ -0,0 +1,34 @@ +// +// KWBeEvaluatedMatcher.h +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/20/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import "KWMessageTrackerMatcher.h" +#import "KWMatchVerifier.h" + +@interface KWBeEvaluatedMatcher : KWMessageTrackerMatcher + +- (void)beEvaluated; +- (void)beEvaluatedWithCount:(NSUInteger)aCount; +- (void)beEvaluatedWithCountAtLeast:(NSUInteger)aCount; +- (void)beEvaluatedWithCountAtMost:(NSUInteger)aCount; + +- (void)beEvaluatedWithArguments:(id)firstArgument, ...; +- (void)beEvaluatedWithCount:(NSUInteger)aCount arguments:(id)firstArgument, ...; +- (void)beEvaluatedWithCountAtLeast:(NSUInteger)aCount arguments:(id)firstArgument, ...; +- (void)beEvaluatedWithCountAtMost:(NSUInteger)aCount arguments:(id)firstArgument, ...; + +@end + +@interface KWMatchVerifier (KWBeEvaluatedMatcherAdditions) + +// NSInvocation doesn't work with va_arg, so we have to implement it directly in match verifier +- (void)beEvaluatedWithArguments:(id)firstArgument, ...; +- (void)beEvaluatedWithCount:(NSUInteger)aCount arguments:(id)firstArgument, ...; +- (void)beEvaluatedWithCountAtLeast:(NSUInteger)aCount arguments:(id)firstArgument, ...; +- (void)beEvaluatedWithCountAtMost:(NSUInteger)aCount arguments:(id)firstArgument, ...; + +@end diff --git a/Classes/Matchers/KWBeEvaluatedMatcher.m b/Classes/Matchers/KWBeEvaluatedMatcher.m new file mode 100644 index 00000000..ad82bbd7 --- /dev/null +++ b/Classes/Matchers/KWBeEvaluatedMatcher.m @@ -0,0 +1,179 @@ +// +// KWBeEvaluatedMatcher.m +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/20/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import "KWBeEvaluatedMatcher.h" + +#import "KWBlockMessagePattern.h" +#import "KWCountType.h" +#import "KWMessageTracker.h" +#import "KWWorkarounds.h" +#import "KWProxyBlock.h" + +#define KWStartVAList(listName, argument) \ + va_list listName; \ + va_start(listName, argument); + +#define KWMessagePatternWithVAListAndSignature(list, argument, signature) \ + [KWBlockMessagePattern messagePatternWithSignature:(signature) \ + firstArgumentFilter:argument \ + argumentList:list] + +#define KWBeEvaluatedWithCount(argument, type, countValue, signature) \ + do { \ + KWStartVAList(args, argument); \ + [(id)self beEvaluatedWithMessagePattern:KWMessagePatternWithVAListAndSignature(args, argument, (signature)) \ + countType:type \ + count:countValue]; \ + } while(0) + +#define KWBeEvaluatedWithUnspecifiedCount(argument, signature) \ + do { \ + KWStartVAList(args, firstArgument) \ + id pattern = KWMessagePatternWithVAListAndSignature(args, firstArgument, (signature)); \ + [(id)self beEvaluatedWithUnspecifiedCountOfMessagePattern:pattern]; \ + } while(0) + +@implementation KWBeEvaluatedMatcher + +#pragma mark - Getting Matcher Strings + ++ (NSArray *)matcherStrings { + return @[@"beEvaluated", + @"beEvaluatedWithCount", + @"beEvaluatedWithCountAtLeast:", + @"beEvaluatedWithCountAtMost:", + @"beEvaluatedWithArguments:", + @"beEvaluatedWithCount:arguments:", + @"beEvaluatedWithCountAtLeast:arguments:", + @"beEvaluatedWithCountAtMost:arguments:", + @"beEvaluatedWithUnspecifiedCountOfMessagePattern:", + @"beEvaluatedWithMessagePattern:countType:count:"]; +} + +#pragma mark - Configuring Matchers + +- (void)beEvaluated { + id pattern = [KWBlockMessagePattern messagePatternWithSignature:[self subjectSignature]]; + [self beEvaluatedWithUnspecifiedCountOfMessagePattern:pattern]; +} + +- (void)beEvaluatedWithCount:(NSUInteger)aCount { + [self beEvaluatedWithCountType:KWCountTypeExact count:aCount]; +} + +- (void)beEvaluatedWithCountAtLeast:(NSUInteger)aCount { + [self beEvaluatedWithCountType:KWCountTypeAtLeast count:aCount]; +} + +- (void)beEvaluatedWithCountAtMost:(NSUInteger)aCount { + [self beEvaluatedWithCountType:KWCountTypeAtMost count:aCount]; +} + +- (void)beEvaluatedWithCountType:(KWCountType)aCountType count:(NSUInteger)aCount { + id pattern = [KWBlockMessagePattern messagePatternWithSignature:[self subjectSignature]]; + + [self beEvaluatedWithMessagePattern:pattern countType:aCountType count:aCount]; +} + +- (void)beEvaluatedWithUnspecifiedCountOfMessagePattern:(KWBlockMessagePattern *)messagePattern { + KWCountType countType = self.willEvaluateAgainstNegativeExpectation ? KWCountTypeAtLeast : KWCountTypeExact; + + [self beEvaluatedWithCountType:countType count:1]; +} + +- (void)beEvaluatedWithArguments:(id)firstArgument, ... { + KWBeEvaluatedWithUnspecifiedCount(firstArgument, [self subjectSignature]); +} + +#define KWBeEvaluatedWithCountType(type) \ + KWBeEvaluatedWithCount(firstArgument, type, aCount, [self subjectSignature]) + +- (void)beEvaluatedWithCount:(NSUInteger)aCount arguments:(id)firstArgument, ... { + KWBeEvaluatedWithCountType(KWCountTypeExact); +} + +- (void)beEvaluatedWithCountAtLeast:(NSUInteger)aCount arguments:(id)firstArgument, ... { + KWBeEvaluatedWithCountType(KWCountTypeAtLeast); +} + +- (void)beEvaluatedWithCountAtMost:(NSUInteger)aCount arguments:(id)firstArgument, ... { + KWBeEvaluatedWithCountType(KWCountTypeAtMost); +} + +#undef KWBeEvaluatedWithCountType + +#pragma mark - Message Pattern Receiving + +- (void)beEvaluatedWithMessagePattern:(KWBlockMessagePattern *)aMessagePattern countType:(KWCountType)aCountType count:(NSUInteger)aCount { +#if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG + @try { +#endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG + + [self setMessageTrackerWithMessagePattern:aMessagePattern countType:aCountType count:aCount]; + +#if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG + } @catch(NSException *exception) { + KWSetExceptionFromAcrossInvocationBoundary(exception); + } +#endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG +} + +#pragma mark - Matching + +- (BOOL)shouldBeEvaluatedAtEndOfExample { + return YES; +} + +- (BOOL)evaluate { + if (![self.subject isKindOfClass:[KWProxyBlock class]]) + [NSException raise:@"KWMatcherException" format:@"subject must be a KWProxyBlock"]; + + return [super evaluate]; +} + +- (NSMethodSignature *)subjectSignature { + return [self.subject methodSignature]; +} + +@end + +@implementation KWMatchVerifier (KWBeEvaluatedMatcherAdditions) + +#pragma mark - Verifying + +- (void)beEvaluatedWithArguments:(id)firstArgument, ... { + KWBeEvaluatedWithUnspecifiedCount(firstArgument, [self beEvaluated_subjectSignature]); +} + +#define KWBeEvaluatedWithCountType(type) \ + KWBeEvaluatedWithCount(firstArgument, type, aCount, [self beEvaluated_subjectSignature]) + +- (void)beEvaluatedWithCount:(NSUInteger)aCount arguments:(id)firstArgument, ... { + KWBeEvaluatedWithCountType(KWCountTypeExact); +} + +- (void)beEvaluatedWithCountAtLeast:(NSUInteger)aCount arguments:(id)firstArgument, ... { + KWBeEvaluatedWithCountType(KWCountTypeAtLeast); +} + +- (void)beEvaluatedWithCountAtMost:(NSUInteger)aCount arguments:(id)firstArgument, ... { + KWBeEvaluatedWithCountType(KWCountTypeAtMost); +} + +#undef KWBeEvaluatedWithCountType + +- (NSMethodSignature *)beEvaluated_subjectSignature { + return [self.subject methodSignature]; +} + +@end + +#undef KWBeEvaluatedWithUnspecifiedCount +#undef KWBeEvaluatedWithCount +#undef KWMessagePatternWithVAListAndSignature +#undef KWStartVAList diff --git a/Classes/Matchers/KWMessageTrackerMatcher.h b/Classes/Matchers/KWMessageTrackerMatcher.h new file mode 100644 index 00000000..a77431f3 --- /dev/null +++ b/Classes/Matchers/KWMessageTrackerMatcher.h @@ -0,0 +1,33 @@ +// +// KWMessageTrackerMatcher.h +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/20/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import "KWMatcher.h" + +#import "KWCountType.h" + +@class KWMessageTracker; +@class KWMessagePattern; + +@interface KWMessageTrackerMatcher : KWMatcher + +#pragma mark - Properties + +@property (nonatomic, readonly) KWMessageTracker *messageTracker; +@property (nonatomic, assign) BOOL willEvaluateMultipleTimes; +@property (nonatomic, assign) BOOL willEvaluateAgainstNegativeExpectation; + +@end + +// methods in this category are used for inheritance and should not be called directly +@interface KWMessageTrackerMatcher (KWKWMessageTrackerMatcherPrivate) + +- (void)setMessageTrackerWithMessagePattern:(KWMessagePattern *)aMessagePattern + countType:(KWCountType)aCountType + count:(NSUInteger)aCount; + +@end diff --git a/Classes/Matchers/KWMessageTrackerMatcher.m b/Classes/Matchers/KWMessageTrackerMatcher.m new file mode 100644 index 00000000..eb387c92 --- /dev/null +++ b/Classes/Matchers/KWMessageTrackerMatcher.m @@ -0,0 +1,72 @@ +// +// KWMessageTrackerMatcher.m +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/20/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import "KWMessageTrackerMatcher.h" + +#import "KWMessageTracker.h" +#import "KWMessagePattern.h" + +@interface KWMessageTrackerMatcher () + +@property (nonatomic, strong) KWMessageTracker *messageTracker; + +@end + +@implementation KWMessageTrackerMatcher + +#pragma mark - Initializing + +- (id)initWithSubject:(id)anObject { + self = [super initWithSubject:anObject]; + if (self) { + _willEvaluateMultipleTimes = NO; + } + + return self; +} + +#pragma mark - Matching + +- (BOOL)shouldBeEvaluatedAtEndOfExample { + return YES; +} + +- (BOOL)evaluate { + BOOL succeeded = [self.messageTracker succeeded]; + + if (!self.willEvaluateMultipleTimes) { + [self.messageTracker stopTracking]; + } + return succeeded; +} + +#pragma mark - Getting Failure Messages + +- (NSString *)failureMessageForShould { + return [NSString stringWithFormat:@"expected subject to receive -%@ %@, but received it %@", + [self.messageTracker.messagePattern stringValue], + [self.messageTracker expectedCountPhrase], + [self.messageTracker receivedCountPhrase]]; +} + +- (NSString *)failureMessageForShouldNot { + return [NSString stringWithFormat:@"expected subject not to receive -%@, but received it %@", + [self.messageTracker.messagePattern stringValue], + [self.messageTracker receivedCountPhrase]]; +} + +#pragma mark - Setting Message Tracker + +- (void)setMessageTrackerWithMessagePattern:(KWMessagePattern *)aMessagePattern + countType:(KWCountType)aCountType + count:(NSUInteger)aCount +{ + self.messageTracker = [KWMessageTracker messageTrackerWithSubject:self.subject messagePattern:aMessagePattern countType:aCountType count:aCount]; +} + +@end diff --git a/Classes/Matchers/KWReceiveMatcher.h b/Classes/Matchers/KWReceiveMatcher.h index 23fa7bff..b1e9e9e0 100644 --- a/Classes/Matchers/KWReceiveMatcher.h +++ b/Classes/Matchers/KWReceiveMatcher.h @@ -6,16 +6,13 @@ #import "KiwiConfiguration.h" #import "KWCountType.h" -#import "KWMatcher.h" +#import "KWMessageTrackerMatcher.h" #import "KWMatchVerifier.h" @class KWMessagePattern; @class KWMessageTracker; -@interface KWReceiveMatcher : KWMatcher - -@property (nonatomic, assign) BOOL willEvaluateMultipleTimes; -@property (nonatomic, assign) BOOL willEvaluateAgainstNegativeExpectation; +@interface KWReceiveMatcher : KWMessageTrackerMatcher #pragma mark - Configuring Matchers diff --git a/Classes/Matchers/KWReceiveMatcher.m b/Classes/Matchers/KWReceiveMatcher.m index 12fb0aef..36bfb10d 100644 --- a/Classes/Matchers/KWReceiveMatcher.m +++ b/Classes/Matchers/KWReceiveMatcher.m @@ -21,25 +21,10 @@ @interface KWReceiveMatcher() -#pragma mark - Properties - -@property (nonatomic, readwrite, strong) KWMessageTracker *messageTracker; - @end @implementation KWReceiveMatcher -#pragma mark - Initializing - -- (id)initWithSubject:(id)anObject { - self = [super initWithSubject:anObject]; - if (self) { - _willEvaluateMultipleTimes = NO; - } - - return self; -} - #pragma mark - Getting Matcher Strings + (NSArray *)matcherStrings { @@ -57,36 +42,6 @@ + (NSArray *)matcherStrings { @"receiveUnspecifiedCountOfMessagePattern:andReturn:"]; } -#pragma mark - Matching - -- (BOOL)shouldBeEvaluatedAtEndOfExample { - return YES; -} - -- (BOOL)evaluate { - BOOL succeeded = [self.messageTracker succeeded]; - - if (!self.willEvaluateMultipleTimes) { - [self.messageTracker stopTracking]; - } - return succeeded; -} - -#pragma mark - Getting Failure Messages - -- (NSString *)failureMessageForShould { - return [NSString stringWithFormat:@"expected subject to receive -%@ %@, but received it %@", - [self.messageTracker.messagePattern stringValue], - [self.messageTracker expectedCountPhrase], - [self.messageTracker receivedCountPhrase]]; -} - -- (NSString *)failureMessageForShouldNot { - return [NSString stringWithFormat:@"expected subject not to receive -%@, but received it %@", - [self.messageTracker.messagePattern stringValue], - [self.messageTracker receivedCountPhrase]]; -} - #pragma mark - Configuring Matchers - (void)receive:(SEL)aSelector { @@ -150,8 +105,8 @@ - (void)receiveMessagePattern:(KWMessagePattern *)aMessagePattern countType:(KWC @try { #endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG - [self.subject stubMessagePattern:aMessagePattern andReturn:nil overrideExisting:NO]; - self.messageTracker = [KWMessageTracker messageTrackerWithSubject:self.subject messagePattern:aMessagePattern countType:aCountType count:aCount]; + [self.subject stubMessagePattern:aMessagePattern andReturn:nil overrideExisting:NO]; + [self setMessageTrackerWithMessagePattern:aMessagePattern countType:aCountType count:aCount]; #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG } @catch(NSException *exception) { @@ -166,7 +121,7 @@ - (void)receiveMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id) #endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG [self.subject stubMessagePattern:aMessagePattern andReturn:aValue]; - self.messageTracker = [KWMessageTracker messageTrackerWithSubject:self.subject messagePattern:aMessagePattern countType:aCountType count:aCount]; + [self setMessageTrackerWithMessagePattern:aMessagePattern countType:aCountType count:aCount]; #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG } @catch(NSException *exception) { diff --git a/Classes/Stubbing/NSObject+KiwiStubAdditions.m b/Classes/Stubbing/NSObject+KiwiStubAdditions.m index 8124687a..4448deff 100644 --- a/Classes/Stubbing/NSObject+KiwiStubAdditions.m +++ b/Classes/Stubbing/NSObject+KiwiStubAdditions.m @@ -8,7 +8,7 @@ #import "KWCaptureSpy.h" #import "KWIntercept.h" #import "KWInvocationCapturer.h" -#import "KWMessagePattern.h" +#import "KWSelectorMessagePattern.h" #import "KWObjCUtilities.h" #import "KWStringUtilities.h" #import "KWStub.h" @@ -32,7 +32,7 @@ - (NSMethodSignature *)invocationCapturer:(KWInvocationCapturer *)anInvocationCa } - (void)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer didCaptureInvocation:(NSInvocation *)anInvocation { - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternFromInvocation:anInvocation]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternFromInvocation:anInvocation]; id value = (anInvocationCapturer.userInfo)[StubValueKey]; if (!(anInvocationCapturer.userInfo)[StubSecondValueKey]) { [self stubMessagePattern:messagePattern andReturn:value]; @@ -46,78 +46,78 @@ - (void)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer didCaptu #pragma mark - Stubbing Methods - (void)stub:(SEL)aSelector { - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternWithSelector:aSelector]; [self stubMessagePattern:messagePattern andReturn:nil]; } - (void)stub:(SEL)aSelector withBlock:(id (^)(NSArray *))block { - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternWithSelector:aSelector]; [self stubMessagePattern:messagePattern withBlock:block]; } - (void)stub:(SEL)aSelector withArguments:(id)firstArgument, ... { va_list argumentList; va_start(argumentList, firstArgument); - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList]; [self stubMessagePattern:messagePattern andReturn:nil]; } - (void)stub:(SEL)aSelector andReturn:(id)aValue { - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternWithSelector:aSelector]; [self stubMessagePattern:messagePattern andReturn:aValue]; } - (void)stub:(SEL)aSelector andReturn:(id)aValue withArguments:(id)firstArgument, ... { va_list argumentList; va_start(argumentList, firstArgument); - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList]; [self stubMessagePattern:messagePattern andReturn:aValue]; } - (void)stub:(SEL)aSelector andReturn:(id)aValue times:(NSNumber *)times afterThatReturn:(id)aSecondValue { - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternWithSelector:aSelector]; [self stubMessagePattern:messagePattern andReturn:aValue times:times afterThatReturn:aSecondValue]; } + (void)stub:(SEL)aSelector { - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternWithSelector:aSelector]; [self stubMessagePattern:messagePattern andReturn:nil]; } + (void)stub:(SEL)aSelector withBlock:(id (^)(NSArray *))block { - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternWithSelector:aSelector]; [self stubMessagePattern:messagePattern withBlock:block]; } + (void)stub:(SEL)aSelector withArguments:(id)firstArgument, ... { va_list argumentList; va_start(argumentList, firstArgument); - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList]; [self stubMessagePattern:messagePattern andReturn:nil]; } + (void)stub:(SEL)aSelector andReturn:(id)aValue { - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternWithSelector:aSelector]; [self stubMessagePattern:messagePattern andReturn:aValue]; } + (void)stub:(SEL)aSelector andReturn:(id)aValue withArguments:(id)firstArgument, ... { va_list argumentList; va_start(argumentList, firstArgument); - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList]; [self stubMessagePattern:messagePattern andReturn:aValue]; } + (void)stub:(SEL)aSelector andReturn:(id)aValue times:(NSNumber *)times afterThatReturn:(id)aSecondValue { - KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector]; + KWSelectorMessagePattern *messagePattern = [KWSelectorMessagePattern messagePatternWithSelector:aSelector]; [self stubMessagePattern:messagePattern andReturn:aValue times:times afterThatReturn:aSecondValue]; } -- (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue { +- (void)stubMessagePattern:(KWSelectorMessagePattern *)aMessagePattern andReturn:(id)aValue { [self stubMessagePattern:aMessagePattern andReturn:aValue overrideExisting:YES]; } -- (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue overrideExisting:(BOOL)overrideExisting { +- (void)stubMessagePattern:(KWSelectorMessagePattern *)aMessagePattern andReturn:(id)aValue overrideExisting:(BOOL)overrideExisting { if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) { [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists", NSStringFromSelector(aMessagePattern.selector)]; @@ -129,7 +129,7 @@ - (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aVa KWAssociateObjectStub(self, stub, overrideExisting); } -- (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue { +- (void)stubMessagePattern:(KWSelectorMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue { if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) { [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists", NSStringFromSelector(aMessagePattern.selector)]; @@ -141,7 +141,7 @@ - (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aVa KWAssociateObjectStub(self, stub, YES); } -- (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block { +- (void)stubMessagePattern:(KWSelectorMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block { if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) { [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists", NSStringFromSelector(aMessagePattern.selector)]; @@ -153,11 +153,11 @@ - (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern withBlock:(id (^) KWAssociateObjectStub(self, stub, YES); } -+ (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue { ++ (void)stubMessagePattern:(KWSelectorMessagePattern *)aMessagePattern andReturn:(id)aValue { [self stubMessagePattern:aMessagePattern andReturn:aValue overrideExisting:YES]; } -+ (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue overrideExisting:(BOOL)override { ++ (void)stubMessagePattern:(KWSelectorMessagePattern *)aMessagePattern andReturn:(id)aValue overrideExisting:(BOOL)override { if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) { [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists", NSStringFromSelector(aMessagePattern.selector)]; @@ -169,11 +169,11 @@ + (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aVa KWAssociateObjectStub(self, stub, override); } -+ (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue { ++ (void)stubMessagePattern:(KWSelectorMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue { [self stubMessagePattern:aMessagePattern andReturn:aValue times:times afterThatReturn:aSecondValue overrideExisting:YES]; } -+ (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue overrideExisting:(BOOL)override { ++ (void)stubMessagePattern:(KWSelectorMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue overrideExisting:(BOOL)override { if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) { [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists", NSStringFromSelector(aMessagePattern.selector)]; @@ -185,11 +185,11 @@ + (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aVa KWAssociateObjectStub(self, stub, override); } -+ (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block { ++ (void)stubMessagePattern:(KWSelectorMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block { [self stubMessagePattern:aMessagePattern withBlock:block overrideExisting:YES]; } -+ (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block overrideExisting:(BOOL)override { ++ (void)stubMessagePattern:(KWSelectorMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block overrideExisting:(BOOL)override { if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) { [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists", NSStringFromSelector(aMessagePattern.selector)]; @@ -207,7 +207,7 @@ - (void)clearStubs { #pragma mark - Spying on Messages -- (void)addMessageSpy:(id)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern { +- (void)addMessageSpy:(id)aSpy forMessagePattern:(KWSelectorMessagePattern *)aMessagePattern { if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) { [NSException raise:@"KWSpyException" format:@"cannot add spy for -%@ because no such method exists", NSStringFromSelector(aMessagePattern.selector)]; @@ -218,11 +218,11 @@ - (void)addMessageSpy:(id)aSpy forMessagePattern:(KWMessagePatt KWAssociateMessageSpy(self, aSpy, aMessagePattern); } -- (void)removeMessageSpy:(id)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern { +- (void)removeMessageSpy:(id)aSpy forMessagePattern:(KWSelectorMessagePattern *)aMessagePattern { KWClearObjectSpy(self, aSpy, aMessagePattern); } -+ (void)addMessageSpy:(id)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern { ++ (void)addMessageSpy:(id)aSpy forMessagePattern:(KWSelectorMessagePattern *)aMessagePattern { if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) { [NSException raise:@"KWSpyException" format:@"cannot add spy for -%@ because no such method exists", NSStringFromSelector(aMessagePattern.selector)]; @@ -233,7 +233,7 @@ + (void)addMessageSpy:(id)aSpy forMessagePattern:(KWMessagePatt KWAssociateMessageSpy(self, aSpy, aMessagePattern); } -+ (void)removeMessageSpy:(id)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern { ++ (void)removeMessageSpy:(id)aSpy forMessagePattern:(KWSelectorMessagePattern *)aMessagePattern { KWClearObjectSpy(self, aSpy, aMessagePattern); } diff --git a/Kiwi.xcodeproj/project.pbxproj b/Kiwi.xcodeproj/project.pbxproj index 1392b8d6..40184c25 100755 --- a/Kiwi.xcodeproj/project.pbxproj +++ b/Kiwi.xcodeproj/project.pbxproj @@ -521,6 +521,44 @@ CE87C52E1AF1994200310C07 /* KWStringUtilitiesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FC83B511B100B100BF98A2 /* KWStringUtilitiesTest.m */; }; CE87C52F1AF1994200310C07 /* KWValueTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F5C6FD2311782A290068BBC8 /* KWValueTest.m */; }; CE87C5301AF1994200310C07 /* NSNumber_KiwiAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA9C69F7190CA6EE002C4DC0 /* NSNumber_KiwiAdditionsTests.m */; }; + D7E699502409850000C3D93C /* KWMessageTrackerMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6994C2409850000C3D93C /* KWMessageTrackerMatcher.m */; }; + D7E699512409850000C3D93C /* KWMessageTrackerMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6994C2409850000C3D93C /* KWMessageTrackerMatcher.m */; }; + D7E699522409850000C3D93C /* KWMessageTrackerMatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E6994D2409850000C3D93C /* KWMessageTrackerMatcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E699532409850000C3D93C /* KWMessageTrackerMatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E6994D2409850000C3D93C /* KWMessageTrackerMatcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E699542409850000C3D93C /* KWBeEvaluatedMatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E6994E2409850000C3D93C /* KWBeEvaluatedMatcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E699552409850000C3D93C /* KWBeEvaluatedMatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E6994E2409850000C3D93C /* KWBeEvaluatedMatcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E699562409850000C3D93C /* KWBeEvaluatedMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6994F2409850000C3D93C /* KWBeEvaluatedMatcher.m */; }; + D7E699572409850000C3D93C /* KWBeEvaluatedMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6994F2409850000C3D93C /* KWBeEvaluatedMatcher.m */; }; + D7E699642409851900C3D93C /* KWBlockInvocation.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E699582409851800C3D93C /* KWBlockInvocation.h */; }; + D7E699652409851900C3D93C /* KWBlockInvocation.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E699582409851800C3D93C /* KWBlockInvocation.h */; }; + D7E699662409851900C3D93C /* KWBlockInvocation.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E699592409851900C3D93C /* KWBlockInvocation.m */; }; + D7E699672409851900C3D93C /* KWBlockInvocation.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E699592409851900C3D93C /* KWBlockInvocation.m */; }; + D7E699682409851900C3D93C /* KWProxyBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E6995A2409851900C3D93C /* KWProxyBlock.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E699692409851900C3D93C /* KWProxyBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E6995A2409851900C3D93C /* KWProxyBlock.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E6996A2409851900C3D93C /* KWBlockMessagePattern.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6995B2409851900C3D93C /* KWBlockMessagePattern.m */; }; + D7E6996B2409851900C3D93C /* KWBlockMessagePattern.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6995B2409851900C3D93C /* KWBlockMessagePattern.m */; }; + D7E6996C2409851900C3D93C /* KWBlockSignature.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6995C2409851900C3D93C /* KWBlockSignature.m */; }; + D7E6996D2409851900C3D93C /* KWBlockSignature.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6995C2409851900C3D93C /* KWBlockSignature.m */; }; + D7E6996E2409851900C3D93C /* KWBlockLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6995D2409851900C3D93C /* KWBlockLayout.m */; }; + D7E6996F2409851900C3D93C /* KWBlockLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6995D2409851900C3D93C /* KWBlockLayout.m */; }; + D7E699702409851900C3D93C /* KWBlockMessagePattern.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E6995E2409851900C3D93C /* KWBlockMessagePattern.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E699712409851900C3D93C /* KWBlockMessagePattern.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E6995E2409851900C3D93C /* KWBlockMessagePattern.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E699722409851900C3D93C /* KWBlockLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E6995F2409851900C3D93C /* KWBlockLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E699732409851900C3D93C /* KWBlockLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E6995F2409851900C3D93C /* KWBlockLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E699742409851900C3D93C /* KWBlockSignature.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E699602409851900C3D93C /* KWBlockSignature.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E699752409851900C3D93C /* KWBlockSignature.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E699602409851900C3D93C /* KWBlockSignature.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E699762409851900C3D93C /* KWProxyBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E699612409851900C3D93C /* KWProxyBlock.m */; }; + D7E699772409851900C3D93C /* KWProxyBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E699612409851900C3D93C /* KWProxyBlock.m */; }; + D7E699782409851900C3D93C /* KWSelectorMessagePattern.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E699622409851900C3D93C /* KWSelectorMessagePattern.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E699792409851900C3D93C /* KWSelectorMessagePattern.h in Headers */ = {isa = PBXBuildFile; fileRef = D7E699622409851900C3D93C /* KWSelectorMessagePattern.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7E6997A2409851900C3D93C /* KWSelectorMessagePattern.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E699632409851900C3D93C /* KWSelectorMessagePattern.m */; }; + D7E6997B2409851900C3D93C /* KWSelectorMessagePattern.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E699632409851900C3D93C /* KWSelectorMessagePattern.m */; }; + D7E699852409938100C3D93C /* KWBlockMessagePatternTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6997F2409936E00C3D93C /* KWBlockMessagePatternTest.m */; }; + D7E699862409938200C3D93C /* KWBlockMessagePatternTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6997F2409936E00C3D93C /* KWBlockMessagePatternTest.m */; }; + D7E699872409938500C3D93C /* KWProxyBlockTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E699802409936E00C3D93C /* KWProxyBlockTest.m */; }; + D7E699882409938600C3D93C /* KWProxyBlockTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E699802409936E00C3D93C /* KWProxyBlockTest.m */; }; + D7E699892409938E00C3D93C /* KWBeEvaluatedMatcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6997C2409934A00C3D93C /* KWBeEvaluatedMatcherTest.m */; }; + D7E6998A2409938F00C3D93C /* KWBeEvaluatedMatcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D7E6997C2409934A00C3D93C /* KWBeEvaluatedMatcherTest.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -819,6 +857,25 @@ CE80E44F1AF255BF00D2F0D6 /* KWBackgroundTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWBackgroundTask.m; sourceTree = ""; }; CE87C4231AF194E900310C07 /* Kiwi.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Kiwi.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CE87C42D1AF194EA00310C07 /* KiwiTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KiwiTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + D7E6994C2409850000C3D93C /* KWMessageTrackerMatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWMessageTrackerMatcher.m; sourceTree = ""; }; + D7E6994D2409850000C3D93C /* KWMessageTrackerMatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWMessageTrackerMatcher.h; sourceTree = ""; }; + D7E6994E2409850000C3D93C /* KWBeEvaluatedMatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWBeEvaluatedMatcher.h; sourceTree = ""; }; + D7E6994F2409850000C3D93C /* KWBeEvaluatedMatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWBeEvaluatedMatcher.m; sourceTree = ""; }; + D7E699582409851800C3D93C /* KWBlockInvocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWBlockInvocation.h; sourceTree = ""; }; + D7E699592409851900C3D93C /* KWBlockInvocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWBlockInvocation.m; sourceTree = ""; }; + D7E6995A2409851900C3D93C /* KWProxyBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWProxyBlock.h; sourceTree = ""; }; + D7E6995B2409851900C3D93C /* KWBlockMessagePattern.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWBlockMessagePattern.m; sourceTree = ""; }; + D7E6995C2409851900C3D93C /* KWBlockSignature.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWBlockSignature.m; sourceTree = ""; }; + D7E6995D2409851900C3D93C /* KWBlockLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWBlockLayout.m; sourceTree = ""; }; + D7E6995E2409851900C3D93C /* KWBlockMessagePattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWBlockMessagePattern.h; sourceTree = ""; }; + D7E6995F2409851900C3D93C /* KWBlockLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWBlockLayout.h; sourceTree = ""; }; + D7E699602409851900C3D93C /* KWBlockSignature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWBlockSignature.h; sourceTree = ""; }; + D7E699612409851900C3D93C /* KWProxyBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWProxyBlock.m; sourceTree = ""; }; + D7E699622409851900C3D93C /* KWSelectorMessagePattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWSelectorMessagePattern.h; sourceTree = ""; }; + D7E699632409851900C3D93C /* KWSelectorMessagePattern.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWSelectorMessagePattern.m; sourceTree = ""; }; + D7E6997C2409934A00C3D93C /* KWBeEvaluatedMatcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWBeEvaluatedMatcherTest.m; sourceTree = ""; }; + D7E6997F2409936E00C3D93C /* KWBlockMessagePatternTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWBlockMessagePatternTest.m; sourceTree = ""; }; + D7E699802409936E00C3D93C /* KWProxyBlockTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWProxyBlockTest.m; sourceTree = ""; }; DA084D3F17E3804200592D5A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; DA084D4217E3804200592D5A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; DA3EAD0718A7A1D700EBBF57 /* KWSharedExampleFunctionalTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWSharedExampleFunctionalTest.m; sourceTree = ""; }; @@ -985,6 +1042,18 @@ 37934CE3177F661400D07AE8 /* Core */ = { isa = PBXGroup; children = ( + D7E699582409851800C3D93C /* KWBlockInvocation.h */, + D7E699592409851900C3D93C /* KWBlockInvocation.m */, + D7E6995F2409851900C3D93C /* KWBlockLayout.h */, + D7E6995D2409851900C3D93C /* KWBlockLayout.m */, + D7E6995E2409851900C3D93C /* KWBlockMessagePattern.h */, + D7E6995B2409851900C3D93C /* KWBlockMessagePattern.m */, + D7E699602409851900C3D93C /* KWBlockSignature.h */, + D7E6995C2409851900C3D93C /* KWBlockSignature.m */, + D7E6995A2409851900C3D93C /* KWProxyBlock.h */, + D7E699612409851900C3D93C /* KWProxyBlock.m */, + D7E699622409851900C3D93C /* KWSelectorMessagePattern.h */, + D7E699632409851900C3D93C /* KWSelectorMessagePattern.m */, 9F982C3A16A802920030A0B1 /* Kiwi.h */, 9F982C3B16A802920030A0B1 /* KiwiBlockMacros.h */, 9F982C3C16A802920030A0B1 /* KiwiConfiguration.h */, @@ -1097,6 +1166,10 @@ 37B0BAFC177F63D000E07085 /* Matchers */ = { isa = PBXGroup; children = ( + D7E6994E2409850000C3D93C /* KWBeEvaluatedMatcher.h */, + D7E6994F2409850000C3D93C /* KWBeEvaluatedMatcher.m */, + D7E6994D2409850000C3D93C /* KWMessageTrackerMatcher.h */, + D7E6994C2409850000C3D93C /* KWMessageTrackerMatcher.m */, 9F982C4716A802920030A0B1 /* KWBeBetweenMatcher.h */, 9F982C4816A802920030A0B1 /* KWBeBetweenMatcher.m */, 9F982C4916A802920030A0B1 /* KWBeEmptyMatcher.h */, @@ -1314,6 +1387,8 @@ F5A1E612117432AC002223E1 /* Support */ = { isa = PBXGroup; children = ( + D7E6997F2409936E00C3D93C /* KWBlockMessagePatternTest.m */, + D7E699802409936E00C3D93C /* KWProxyBlockTest.m */, 4AD7A2091962AC8F005ED93F /* Config.m */, F5D7C8D311643C2900758FEA /* KWDeviceInfoTest.m */, 89861D9316FE0EE5008CE99D /* KWFormatterTest.m */, @@ -1330,6 +1405,7 @@ F5A9EFD211741E7700E9EDA3 /* Matchers */ = { isa = PBXGroup; children = ( + D7E6997C2409934A00C3D93C /* KWBeEvaluatedMatcherTest.m */, F553B2D211756157004FCA2E /* KWBeBetweenMatcherTest.m */, F553B4411175A190004FCA2E /* KWBeEmptyMatcherTest.m */, F5E50320119EA22F00F5D05F /* KWBeIndenticalToMatcherTest.m */, @@ -1380,6 +1456,13 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + D7E699532409850000C3D93C /* KWMessageTrackerMatcher.h in Headers */, + D7E699552409850000C3D93C /* KWBeEvaluatedMatcher.h in Headers */, + D7E699792409851900C3D93C /* KWSelectorMessagePattern.h in Headers */, + D7E699712409851900C3D93C /* KWBlockMessagePattern.h in Headers */, + D7E699752409851900C3D93C /* KWBlockSignature.h in Headers */, + D7E699692409851900C3D93C /* KWProxyBlock.h in Headers */, + D7E699732409851900C3D93C /* KWBlockLayout.h in Headers */, 4AE0302E1AEB47FE00556381 /* KWSuiteConfigurationBase.h in Headers */, 4AE030301AEB47FE00556381 /* KiwiBlockMacros.h in Headers */, 4AE030311AEB47FE00556381 /* KiwiConfiguration.h in Headers */, @@ -1457,20 +1540,14 @@ 4AE030881AEB480800556381 /* KWStub.h in Headers */, 4AE0308A1AEB480900556381 /* NSObject+KiwiStubAdditions.h in Headers */, CE80E4511AF255BF00D2F0D6 /* KWBackgroundTask.h in Headers */, - 4AE0308B1AEB480900556381 /* KWAsyncVerifier.h in Headers */, - 4AE0303B1AEB47FE00556381 /* KWExampleDelegate.h in Headers */, - 4AE0308C1AEB480900556381 /* KWExistVerifier.h in Headers */, 4AE0303D1AEB47FE00556381 /* KWExampleSuite.h in Headers */, 4AE0306E1AEB480400556381 /* KWGenericMatchEvaluator.h in Headers */, 4AE0306F1AEB480400556381 /* KWGenericMatchingAdditions.h in Headers */, - 4AE0308D1AEB480900556381 /* KWMatchVerifier.h in Headers */, 4AE030891AEB480900556381 /* KWIntercept.h in Headers */, 4AE030831AEB480700556381 /* KWLetNode.h in Headers */, - 4AE0308E1AEB480A00556381 /* KWVerifying.h in Headers */, - 4AE030461AEB47FF00556381 /* KWMessageTracker.h in Headers */, - 4AE030491AEB47FF00556381 /* KWProbe.h in Headers */, 4AE0304A1AEB480000556381 /* KWProbePoller.h in Headers */, - 4AE0304B1AEB480000556381 /* KWReporting.h in Headers */, + 4AE030461AEB47FF00556381 /* KWMessageTracker.h in Headers */, + D7E699652409851900C3D93C /* KWBlockInvocation.h in Headers */, 4AE030871AEB480800556381 /* KWSharedExampleRegistry.h in Headers */, 4AE0302F1AEB47FE00556381 /* KWSymbolicator.h in Headers */, 4AE0304F1AEB480000556381 /* KWWorkarounds.h in Headers */, @@ -1479,6 +1556,13 @@ 4AE030561AEB480100556381 /* NSMethodSignature+KiwiAdditions.h in Headers */, 4AE030571AEB480100556381 /* NSNumber+KiwiAdditions.h in Headers */, 4AE0305B1AEB480100556381 /* NSValue+KiwiAdditions.h in Headers */, + 4AE0308B1AEB480900556381 /* KWAsyncVerifier.h in Headers */, + 4AE0303B1AEB47FE00556381 /* KWExampleDelegate.h in Headers */, + 4AE0308C1AEB480900556381 /* KWExistVerifier.h in Headers */, + 4AE0308D1AEB480900556381 /* KWMatchVerifier.h in Headers */, + 4AE0308E1AEB480A00556381 /* KWVerifying.h in Headers */, + 4AE030491AEB47FF00556381 /* KWProbe.h in Headers */, + 4AE0304B1AEB480000556381 /* KWReporting.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1486,6 +1570,13 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + D7E699782409851900C3D93C /* KWSelectorMessagePattern.h in Headers */, + D7E699542409850000C3D93C /* KWBeEvaluatedMatcher.h in Headers */, + D7E699722409851900C3D93C /* KWBlockLayout.h in Headers */, + D7E699522409850000C3D93C /* KWMessageTrackerMatcher.h in Headers */, + D7E699742409851900C3D93C /* KWBlockSignature.h in Headers */, + D7E699682409851900C3D93C /* KWProxyBlock.h in Headers */, + D7E699702409851900C3D93C /* KWBlockMessagePattern.h in Headers */, CE87C4971AF1963B00310C07 /* KiwiMacros.h in Headers */, CE87C4951AF1963B00310C07 /* KiwiBlockMacros.h in Headers */, CE87C4BE1AF1963B00310C07 /* NSProxy+KiwiVerifierAdditions.h in Headers */, @@ -1569,6 +1660,8 @@ CE87C4941AF1963B00310C07 /* Kiwi.h in Headers */, CE87C4AD1AF1963B00310C07 /* KWProbe.h in Headers */, CE87C4AC1AF1963B00310C07 /* KWObjCUtilities.h in Headers */, + CE87C4AA1AF1963B00310C07 /* KWMessageTracker.h in Headers */, + D7E699642409851900C3D93C /* KWBlockInvocation.h in Headers */, CE87C4F11AF1963B00310C07 /* KWMatchVerifier.h in Headers */, CE87C49C1AF1963B00310C07 /* KWCountType.h in Headers */, CE87C4C61AF1963B00310C07 /* KWNilMatcher.h in Headers */, @@ -1577,7 +1670,6 @@ CE87C4D31AF1963B00310C07 /* KWGenericMatchingAdditions.h in Headers */, CE87C4B31AF1963B00310C07 /* KWWorkarounds.h in Headers */, CE87C4AE1AF1963B00310C07 /* KWProbePoller.h in Headers */, - CE87C4AA1AF1963B00310C07 /* KWMessageTracker.h in Headers */, CE87C4921AF1960C00310C07 /* KWExampleSuite.h in Headers */, CE87C4911AF195FC00310C07 /* KWSymbolicator.h in Headers */, CE87C4B81AF1963B00310C07 /* NSInvocation+KiwiAdditions.h in Headers */, @@ -1769,16 +1861,20 @@ 4AE02FE91AEB47E600556381 /* KWMessagePattern.m in Sources */, 4AE02FEA1AEB47E600556381 /* KWMessageTracker.m in Sources */, 4AE02FEB1AEB47E600556381 /* KWNull.m in Sources */, + D7E6996D2409851900C3D93C /* KWBlockSignature.m in Sources */, 4AE02FEC1AEB47E600556381 /* KWObjCUtilities.m in Sources */, 4AE02FED1AEB47E600556381 /* KWProbePoller.m in Sources */, CE80E4531AF255BF00D2F0D6 /* KWBackgroundTask.m in Sources */, 4AE02FEE1AEB47E600556381 /* KWSpec.m in Sources */, + D7E6997B2409851900C3D93C /* KWSelectorMessagePattern.m in Sources */, 4AE02FEF1AEB47E600556381 /* KWStringUtilities.m in Sources */, 4AE02FF01AEB47E600556381 /* KWValue.m in Sources */, 4AE02FF11AEB47E600556381 /* KWWorkarounds.m in Sources */, 4AE02FF21AEB47E600556381 /* KWMatcher.m in Sources */, + D7E699572409850000C3D93C /* KWBeEvaluatedMatcher.m in Sources */, 4AE02FF31AEB47E600556381 /* KWMatcherFactory.m in Sources */, 4AE02FF41AEB47E600556381 /* KWMatchers.m in Sources */, + D7E699672409851900C3D93C /* KWBlockInvocation.m in Sources */, 4AE02FF51AEB47E600556381 /* NSInvocation+KiwiAdditions.m in Sources */, 4AE02FF61AEB47E600556381 /* NSInvocation+OCMAdditions.m in Sources */, 4AE02FF71AEB47E600556381 /* NSMethodSignature+KiwiAdditions.m in Sources */, @@ -1786,7 +1882,9 @@ 4AE02FFA1AEB47E600556381 /* NSObject+KiwiVerifierAdditions.m in Sources */, 4AE02FFB1AEB47E600556381 /* NSProxy+KiwiVerifierAdditions.m in Sources */, 4AE02FFC1AEB47E600556381 /* NSValue+KiwiAdditions.m in Sources */, + D7E699512409850000C3D93C /* KWMessageTrackerMatcher.m in Sources */, 4AE02FFD1AEB47E600556381 /* KWNotificationMatcher.m in Sources */, + D7E699772409851900C3D93C /* KWProxyBlock.m in Sources */, 4AE02FFE1AEB47E600556381 /* KWBeBetweenMatcher.m in Sources */, 4AE02FFF1AEB47E600556381 /* KWBeEmptyMatcher.m in Sources */, 4AE030001AEB47E600556381 /* KWBeIdenticalToMatcher.m in Sources */, @@ -1801,6 +1899,7 @@ 4AE030091AEB47E600556381 /* KWChangeMatcher.m in Sources */, 4AE0300A1AEB47E600556381 /* KWConformToProtocolMatcher.m in Sources */, 4AE0300B1AEB47E600556381 /* KWContainMatcher.m in Sources */, + D7E6996F2409851900C3D93C /* KWBlockLayout.m in Sources */, 4AE0300C1AEB47E600556381 /* KWContainStringMatcher.m in Sources */, 4AE0300D1AEB47E600556381 /* KWEqualMatcher.m in Sources */, 4AE0300E1AEB47E600556381 /* KWGenericMatcher.m in Sources */, @@ -1818,6 +1917,7 @@ 4AE030191AEB47E600556381 /* KWUserDefinedMatcher.m in Sources */, 4AE0301A1AEB47E600556381 /* KWMock.m in Sources */, 4AE0301B1AEB47E600556381 /* NSObject+KiwiMockAdditions.m in Sources */, + D7E6996B2409851900C3D93C /* KWBlockMessagePattern.m in Sources */, 4AE0301C1AEB47E600556381 /* KWAfterAllNode.m in Sources */, 4AE0301D1AEB47E600556381 /* KWAfterEachNode.m in Sources */, 4AE0301E1AEB47E600556381 /* KWBeforeAllNode.m in Sources */, @@ -1854,6 +1954,7 @@ 4AE030951AEB494400556381 /* KWBeTrueMatcherTest.m in Sources */, 4AE030961AEB494400556381 /* KWBeWithinMatcherTest.m in Sources */, 4AE030971AEB494400556381 /* KWBlockRaiseMatcherTest.m in Sources */, + D7E699862409938200C3D93C /* KWBlockMessagePatternTest.m in Sources */, 4B9314A623D0E83C007A295C /* KWNotificationMatcherTest.m in Sources */, 4AE030981AEB494400556381 /* KWChangeMatcherTest.m in Sources */, 4AE030991AEB494400556381 /* KWConformToProtocolMatcherTest.m in Sources */, @@ -1864,9 +1965,11 @@ 4AE0309D1AEB494400556381 /* KWGenericMatcherTest.m in Sources */, 4AE0309E1AEB494400556381 /* KWHaveMatcherTest.m in Sources */, CE7EFF9A1B8475BD00FFE6D6 /* KWSwiftXCTestAssertionTests.swift in Sources */, + D7E699882409938600C3D93C /* KWProxyBlockTest.m in Sources */, 4AE0309F1AEB494400556381 /* KWHaveValueMatcherTest.m in Sources */, 4AE030A01AEB494400556381 /* KWInequalityMatcherTest.m in Sources */, 4AE030A11AEB494400556381 /* KWReceiveMatcherTest.m in Sources */, + D7E6998A2409938F00C3D93C /* KWBeEvaluatedMatcherTest.m in Sources */, 4AE030A21AEB494400556381 /* KWRegularExpressionPatternMatcherTest.m in Sources */, 4AE030A31AEB494400556381 /* KWRespondToSelectorMatcherTest.m in Sources */, 4AE030A41AEB494400556381 /* KWUserDefinedMatcherTest.m in Sources */, @@ -1936,16 +2039,20 @@ CE87C44C1AF195BE00310C07 /* KWMessagePattern.m in Sources */, CE87C44D1AF195BE00310C07 /* KWMessageTracker.m in Sources */, CE87C44E1AF195BE00310C07 /* KWNull.m in Sources */, + D7E6996C2409851900C3D93C /* KWBlockSignature.m in Sources */, CE87C44F1AF195BE00310C07 /* KWObjCUtilities.m in Sources */, CE87C4501AF195BE00310C07 /* KWProbePoller.m in Sources */, CE80E4521AF255BF00D2F0D6 /* KWBackgroundTask.m in Sources */, CE87C4511AF195BE00310C07 /* KWSpec.m in Sources */, + D7E6997A2409851900C3D93C /* KWSelectorMessagePattern.m in Sources */, CE87C4521AF195BE00310C07 /* KWStringUtilities.m in Sources */, CE87C4531AF195BE00310C07 /* KWValue.m in Sources */, CE87C4541AF195BE00310C07 /* KWWorkarounds.m in Sources */, CE87C4551AF195BE00310C07 /* KWMatcher.m in Sources */, + D7E699562409850000C3D93C /* KWBeEvaluatedMatcher.m in Sources */, CE87C4561AF195BE00310C07 /* KWMatcherFactory.m in Sources */, CE87C4571AF195BE00310C07 /* KWMatchers.m in Sources */, + D7E699662409851900C3D93C /* KWBlockInvocation.m in Sources */, CE87C4581AF195BE00310C07 /* NSInvocation+KiwiAdditions.m in Sources */, CE87C4591AF195BE00310C07 /* NSInvocation+OCMAdditions.m in Sources */, CE87C45A1AF195BE00310C07 /* NSMethodSignature+KiwiAdditions.m in Sources */, @@ -1953,7 +2060,9 @@ CE87C45C1AF195BE00310C07 /* NSObject+KiwiSpyAdditions.m in Sources */, CE87C45D1AF195BE00310C07 /* NSObject+KiwiVerifierAdditions.m in Sources */, CE87C45E1AF195BE00310C07 /* NSProxy+KiwiVerifierAdditions.m in Sources */, + D7E699502409850000C3D93C /* KWMessageTrackerMatcher.m in Sources */, CE87C45F1AF195BE00310C07 /* NSValue+KiwiAdditions.m in Sources */, + D7E699762409851900C3D93C /* KWProxyBlock.m in Sources */, CE87C4601AF195BE00310C07 /* KWNotificationMatcher.m in Sources */, CE87C4611AF195BE00310C07 /* KWBeBetweenMatcher.m in Sources */, CE87C4621AF195BE00310C07 /* KWBeEmptyMatcher.m in Sources */, @@ -1968,6 +2077,7 @@ CE87C46B1AF195BE00310C07 /* KWBlockRaiseMatcher.m in Sources */, CE87C46C1AF195BE00310C07 /* KWChangeMatcher.m in Sources */, CE87C46D1AF195BE00310C07 /* KWConformToProtocolMatcher.m in Sources */, + D7E6996E2409851900C3D93C /* KWBlockLayout.m in Sources */, CE87C46E1AF195BE00310C07 /* KWContainMatcher.m in Sources */, CE87C46F1AF195BE00310C07 /* KWContainStringMatcher.m in Sources */, CE87C4701AF195BE00310C07 /* KWEqualMatcher.m in Sources */, @@ -1985,6 +2095,7 @@ CE87C47C1AF195BE00310C07 /* KWUserDefinedMatcher.m in Sources */, CE87C47D1AF195BE00310C07 /* KWMock.m in Sources */, CE87C47E1AF195BE00310C07 /* NSObject+KiwiMockAdditions.m in Sources */, + D7E6996A2409851900C3D93C /* KWBlockMessagePattern.m in Sources */, CE87C47F1AF195BE00310C07 /* KWAfterAllNode.m in Sources */, CE87C4801AF195BE00310C07 /* KWAfterEachNode.m in Sources */, CE87C4811AF195BE00310C07 /* KWBeforeAllNode.m in Sources */, @@ -2021,6 +2132,7 @@ CE87C4FB1AF1994100310C07 /* KWBeWithinMatcherTest.m in Sources */, CE87C4FC1AF1994100310C07 /* KWBlockRaiseMatcherTest.m in Sources */, CE87C4FD1AF1994100310C07 /* KWChangeMatcherTest.m in Sources */, + D7E699852409938100C3D93C /* KWBlockMessagePatternTest.m in Sources */, 4B9314A523D0E83C007A295C /* KWNotificationMatcherTest.m in Sources */, CE87C4FE1AF1994100310C07 /* KWConformToProtocolMatcherTest.m in Sources */, CE87C4FF1AF1994100310C07 /* KWContainMatcherTest.m in Sources */, @@ -2031,9 +2143,11 @@ CE87C5031AF1994100310C07 /* KWHaveMatcherTest.m in Sources */, CE87C5041AF1994100310C07 /* KWHaveValueMatcherTest.m in Sources */, CE7EFF991B8475BD00FFE6D6 /* KWSwiftXCTestAssertionTests.swift in Sources */, + D7E699872409938500C3D93C /* KWProxyBlockTest.m in Sources */, CE87C5051AF1994100310C07 /* KWInequalityMatcherTest.m in Sources */, CE87C5061AF1994100310C07 /* KWReceiveMatcherTest.m in Sources */, CE87C5071AF1994100310C07 /* KWRegularExpressionPatternMatcherTest.m in Sources */, + D7E699892409938E00C3D93C /* KWBeEvaluatedMatcherTest.m in Sources */, CE87C5081AF1994100310C07 /* KWRespondToSelectorMatcherTest.m in Sources */, CE87C5091AF1994100310C07 /* KWUserDefinedMatcherTest.m in Sources */, CE87C50A1AF1994100310C07 /* KWBeZeroMatcherTest.m in Sources */, diff --git a/Tests/KWBeEvaluatedMatcherTest.m b/Tests/KWBeEvaluatedMatcherTest.m new file mode 100644 index 00000000..9133a67f --- /dev/null +++ b/Tests/KWBeEvaluatedMatcherTest.m @@ -0,0 +1,269 @@ +// +// KWBeEvaluatedMatcherTest.m +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/21/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import +#import "KiwiTestConfiguration.h" +#import "TestClasses.h" + +#if KW_TESTS_ENABLED + +typedef void(^KWTestBlock)(id); + +@interface KWBeEvaluatedMatcherTest : XCTestCase +@property (nonatomic, strong) id subject; +@property (nonatomic, strong) id parameter; +@property (nonatomic, readonly) KWBeEvaluatedMatcher *matcher; +@property (nonatomic, readonly) KWBeEvaluatedMatcher *negativeExpecationMatcher; + +@end + +@implementation KWBeEvaluatedMatcherTest + +@dynamic matcher; +@dynamic negativeExpecationMatcher; + +- (void)setUp { + [super setUp]; + + self.parameter = [NSObject new]; + + id subject = [KWProxyBlock blockWithBlock:^(id object) { [object description]; }]; + + self.subject = subject; +} + +- (KWBeEvaluatedMatcher *)matcher { + return [KWBeEvaluatedMatcher matcherWithSubject:self.subject]; +} + +- (KWBeEvaluatedMatcher *)negativeExpecationMatcher { + KWBeEvaluatedMatcher *matcher = self.matcher; + matcher.willEvaluateAgainstNegativeExpectation = YES; + + return matcher; +} + +- (void)callSubject { + id subject = self.subject; + + if (subject) { + ((KWTestBlock)subject)(self.parameter); + } +} + +- (void)callSubjectWithCount:(NSUInteger)count { + for (NSUInteger i = 0; i < count; i++) { + [self callSubject]; + } +} + +- (void)testItShouldHaveTheRightMatcherStrings { + id matcherStrings = [KWBeEvaluatedMatcher matcherStrings]; + id expectedStrings = @[@"beEvaluated", + @"beEvaluatedWithCount", + @"beEvaluatedWithCountAtLeast:", + @"beEvaluatedWithCountAtMost:", + @"beEvaluatedWithArguments:", + @"beEvaluatedWithCount:arguments:", + @"beEvaluatedWithCountAtLeast:arguments:", + @"beEvaluatedWithCountAtMost:arguments:", + @"beEvaluatedWithUnspecifiedCountOfMessagePattern:", + @"beEvaluatedWithMessagePattern:countType:count:"]; + + XCTAssertEqualObjects([matcherStrings sortedArrayUsingSelector:@selector(compare:)], + [expectedStrings sortedArrayUsingSelector:@selector(compare:)], + @"expected specific matcher strings"); +} + +#pragma mark - beEvaluated testing + +- (void)testItShouldMatchEvaluationsForBeEvaluated { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluated]; + + [self callSubject]; + + XCTAssertTrue([matcher evaluate], @"expected positive match"); +} + +- (void)testItShouldMatchMultipleEvaluationsForBeEvaluatedWhenAttachedToNegativeVerifier { + KWBeEvaluatedMatcher *matcher = self.negativeExpecationMatcher; + [matcher beEvaluated]; + + [self callSubjectWithCount:2]; + + XCTAssertTrue([matcher evaluate], @"expected positive match"); +} + +- (void)testItShouldNotMatchMultipleEvaluationsForBeEvaluatedWhenNotAttachedToNegativeVerifier { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluated]; + [matcher beEvaluated]; + + [self callSubjectWithCount:2]; + + XCTAssertFalse([matcher evaluate], @"expected negative match"); +} + +- (void)testItShouldMatchEvaluationsForBeEvaluatedWithCount { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithCount:2]; + + [self callSubjectWithCount:2]; + + XCTAssertTrue([matcher evaluate], @"expected positive match"); +} + +- (void)testItShouldNotMatchEvaluationsForBeEvaluatedWithInappropriateCount { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithCount:2]; + + [self callSubjectWithCount:3]; + + XCTAssertFalse([matcher evaluate], @"expected negative match"); +} + +- (void)testItShouldMatchEvaluationsForBeEvaluatedWithCountAtLeast { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithCountAtLeast:2]; + + [self callSubjectWithCount:3]; + + XCTAssertTrue([matcher evaluate], @"expected positive match"); +} + +- (void)testItShouldNotMatchEvaluationsForBeEvaluatedWithInappropriateCountAtLeast { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithCountAtLeast:2]; + + [self callSubjectWithCount:1]; + + XCTAssertFalse([matcher evaluate], @"expected negative match"); +} + +- (void)testItShouldMatchEvaluationsForBeEvaluatedWithCountAtMost { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithCountAtMost:3]; + + [self callSubjectWithCount:2]; + + XCTAssertTrue([matcher evaluate], @"expected positive match"); +} + +- (void)testItShouldNotMatchEvaluationsForBeEvaluatedWithInappropriateCountAtMost { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithCountAtMost:2]; + + [self callSubjectWithCount:3]; + + XCTAssertFalse([matcher evaluate], @"expected negative match"); +} + +#pragma mark - beEvaluatedWithParameters + +- (void)testItShouldMatchEvaluationsAndParametersForBeEvaluated { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithArguments:self.parameter]; + + [self callSubject]; + + XCTAssertTrue([matcher evaluate], @"expected positive match"); +} + +- (void)testItShouldMatchMultipleEvaluationsAndParametersForBeEvaluatedWhenAttachedToNegativeVerifier { + KWBeEvaluatedMatcher *matcher = self.negativeExpecationMatcher; + [matcher beEvaluatedWithArguments:self.parameter]; + + [self callSubjectWithCount:2]; + + XCTAssertTrue([matcher evaluate], @"expected positive match"); +} + +- (void)testItShouldNotMatchMultipleEvaluationsAndParametersForBeEvaluatedWhenNotAttachedToNegativeVerifier { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithArguments:self.parameter]; + [matcher beEvaluatedWithArguments:self.parameter]; + + [self callSubjectWithCount:2]; + + XCTAssertFalse([matcher evaluate], @"expected negative match"); +} + +- (void)testItShouldMatchEvaluationsAndParametersForBeEvaluatedWithCount { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithCount:2 arguments:self.parameter]; + + [self callSubjectWithCount:2]; + + XCTAssertTrue([matcher evaluate], @"expected positive match"); +} + +- (void)testItShouldNotMatchEvaluationsAndParametersForBeEvaluatedWithInappropriateCount { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithCount:2 arguments:self.parameter]; + + [self callSubjectWithCount:3]; + + XCTAssertFalse([matcher evaluate], @"expected negative match"); +} + +- (void)testItShouldMatchEvaluationsAndParametersForBeEvaluatedWithCountAtLeast { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithCountAtLeast:2 arguments:self.parameter]; + + [self callSubjectWithCount:3]; + + XCTAssertTrue([matcher evaluate], @"expected positive match"); +} + +- (void)testItShouldNotMatchEvaluationsAndParametersForBeEvaluatedWithInappropriateCountAtLeast { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithCountAtLeast:2 arguments:self.parameter]; + + [self callSubjectWithCount:1]; + + XCTAssertFalse([matcher evaluate], @"expected negative match"); +} + +- (void)testItShouldMatchEvaluationsAndParametersForBeEvaluatedWithCountAtMost { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithCountAtMost:3 arguments:self.parameter]; + + [self callSubjectWithCount:2]; + + XCTAssertTrue([matcher evaluate], @"expected positive match"); +} + +- (void)testItShouldNotMatchEvaluationsAndParametersForBeEvaluatedWithInappropriateCountAtMost { + KWBeEvaluatedMatcher *matcher = self.matcher; + + [matcher beEvaluatedWithCountAtMost:2 arguments:self.parameter]; + + [self callSubjectWithCount:3]; + + XCTAssertFalse([matcher evaluate], @"expected negative match"); +} + +@end + +#endif // #if KW_TESTS_ENABLED diff --git a/Tests/KWBlockMessagePatternTest.m b/Tests/KWBlockMessagePatternTest.m new file mode 100644 index 00000000..3625a153 --- /dev/null +++ b/Tests/KWBlockMessagePatternTest.m @@ -0,0 +1,163 @@ +// +// KWBlockMessagePatternTest.m +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/26/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import + +#import "KiwiTestConfiguration.h" +#import "NSMethodSignature+KiwiAdditions.h" +#import "TestClasses.h" +#import "KWBlockInvocation.h" + +#if KW_TESTS_ENABLED + +static NSString * const kKWFoo = @"foo"; +static NSString * const kKWBar = @"bar"; + +typedef void(^KWTestBlock)(id, id); + +@interface KWBlockMessagePatternTest : XCTestCase +@property (nonatomic) KWProxyBlock *block; +@property (nonatomic, readonly) NSMethodSignature *blockSignature; + +- (NSInvocation *)blockInvocationWithArguments:(id)firstBytes, ...; + +@end + +@implementation KWBlockMessagePatternTest + +@dynamic blockSignature; + +- (NSMethodSignature *)blockSignature { + return self.block.methodSignature; +} + +- (NSInvocation *)blockInvocationWithArguments:(id)firstBytes, ... { + NSMethodSignature *blockSignature = self.blockSignature; + NSUInteger numberOfMessageArguments = [blockSignature numberOfMessageArguments]; + + NSInvocation *invocation = [KWBlockInvocation invocationWithTarget:self.block]; + if (numberOfMessageArguments == 0) + return invocation; + + va_list argumentList; + va_start(argumentList, firstBytes); + id bytes = firstBytes; + + NSUInteger vaArgCount = numberOfMessageArguments - 1; + for (NSUInteger i = 0; i < vaArgCount; ++i) { + [invocation setMessageArgument:&bytes atIndex:i]; + bytes = va_arg(argumentList, id); + } + + [invocation setMessageArgument:&bytes atIndex:vaArgCount]; + + va_end(argumentList); + + return invocation; +} + +- (void)setUp { + [super setUp]; + + self.block = [KWProxyBlock blockWithBlock:^(id object1, id object2) { [object1 description]; }]; +} + +- (KWBlockMessagePattern *)messagePatternWithArguments:(id)firstArgument, ... { + va_list argumentList; + va_start(argumentList, firstArgument); + + return [KWBlockMessagePattern messagePatternWithSignature:self.block.methodSignature + firstArgumentFilter:firstArgument + argumentList:argumentList]; +} + +- (void)testItShouldCreateMessagePatternsWithVarArgs { + id value = [KWValue valueWithUnsignedInt:1]; + KWBlockMessagePattern *messagePattern = [self messagePatternWithArguments:nil, value]; + + NSArray *filters = messagePattern.argumentFilters; + + XCTAssertEqualObjects(filters[0], [KWNull null], @"expected matching argument"); + XCTAssertEqualObjects(filters[1], value, @"expected matching argument"); +} + +- (void)testItShouldMatchInvocationsWithNilArguments { + KWBlockMessagePattern *messagePattern = [self messagePatternWithArguments:kKWFoo, nil]; + + NSInvocation *invocation = [self blockInvocationWithArguments:kKWFoo, nil]; + + XCTAssertTrue([messagePattern matchesInvocation:invocation], @"expected matching invocation"); +} + +- (void)testItShouldMatchInvocationsWithAnyArguments { + KWBlockMessagePattern *messagePattern = [self messagePatternWithArguments:kKWFoo, [KWAny any]]; + + NSInvocation *invocation = [self blockInvocationWithArguments:kKWFoo, kKWBar]; + + XCTAssertTrue([messagePattern matchesInvocation:invocation], @"expected matching invocation"); +} + +- (void)testItShouldMatchInvocationsWithClassArgument { + KWBlockMessagePattern *messagePattern = [self messagePatternWithArguments:[self class], nil]; + + NSInvocation *invocation = [self blockInvocationWithArguments:[self class], nil]; + + XCTAssertTrue([messagePattern matchesInvocation:invocation], @"expected matching invocation"); +} + +- (void)testItShouldMatchInvocationsWithAnyArgumentsWhenCreatedWithMessagePatternFromInvocation { + NSInvocation *creationInvocation = [self blockInvocationWithArguments:kKWFoo, [KWAny any]]; + + KWBlockMessagePattern *messagePattern = [KWBlockMessagePattern messagePatternFromInvocation:creationInvocation]; + + NSInvocation *invocation = [self blockInvocationWithArguments:kKWFoo, kKWBar]; + + XCTAssertTrue([messagePattern matchesInvocation:invocation], @"expected matching invocation"); +} + +- (void)testItShouldMatchInvocationsWithAnyArgumentsForBlockParameters { + NSInvocation *creationInvocation = [self blockInvocationWithArguments:kKWFoo, [KWAny any]]; + + KWBlockMessagePattern *messagePattern = [KWBlockMessagePattern messagePatternFromInvocation:creationInvocation]; + + NSInvocation *invocation = [self blockInvocationWithArguments:kKWFoo, ^{}]; + + XCTAssertTrue([messagePattern matchesInvocation:invocation], @"expected matching invocation"); + +} + +- (void)testItShouldNotMatchInvocationsWithAnyArguments { + KWBlockMessagePattern *messagePattern = [self messagePatternWithArguments:kKWBar, [KWAny any]]; + + NSInvocation *invocation = [self blockInvocationWithArguments:kKWFoo, kKWBar]; + + XCTAssertFalse([messagePattern matchesInvocation:invocation], @"expected non-matching invocation"); +} + +- (void)testItShouldNotMatchInvocationsWithDifferentArguments { + KWBlockMessagePattern *messagePattern = [self messagePatternWithArguments:kKWFoo, nil]; + + NSInvocation *invocation = [self blockInvocationWithArguments:kKWFoo, kKWBar]; + + XCTAssertFalse([messagePattern matchesInvocation:invocation], @"expected non-matching invocation"); +} + +- (void)testItShouldCompareMessagePatternsWithNilAndNonNilArgumentFilters { + KWBlockMessagePattern *messagePattern1 = [KWBlockMessagePattern messagePatternWithSignature:self.blockSignature]; + + NSArray *argumentFilters = @[[KWValue valueWithUnsignedInt:42]]; + KWBlockMessagePattern *messagePattern2 = [KWBlockMessagePattern messagePatternWithSignature:self.blockSignature + argumentFilters:argumentFilters]; + + XCTAssertFalse([messagePattern1 isEqual:messagePattern2], @"expected message patterns to compare as not equal"); + XCTAssertFalse([messagePattern2 isEqual:messagePattern1], @"expected message patterns to compare as not equal"); +} + +@end + +#endif // #if KW_TESTS_ENABLED diff --git a/Tests/KWMessagePatternFunctionalTests.m b/Tests/KWMessagePatternFunctionalTests.m index f4c2dd99..94976756 100644 --- a/Tests/KWMessagePatternFunctionalTests.m +++ b/Tests/KWMessagePatternFunctionalTests.m @@ -2,17 +2,80 @@ #import "KiwiTestConfiguration.h" #import "TestClasses.h" +typedef void(^KWVoidTestBlock)(void); +typedef void(^KWArgumentTestBlock)(id); +typedef void(^KWMultiArgumentTestBlock)(id, id, id); + SPEC_BEGIN(KWMessagePatternFunctionalTests) describe(@"message patterns", ^{ + NSString *description = @"description"; + NSString *format = nil; + Cruiser *cruiser = [Cruiser new]; + + it(@"can match a selector with no arguments", ^{ + [[cruiser should] receive:@selector(computeParsecs)]; + + [cruiser computeParsecs]; + }); + it(@"can match a selector with a specific single argument", ^{ - Cruiser *cruiser = [Cruiser new]; Fighter *fighter = [Fighter mock]; + [[cruiser should] receive:@selector(loadFighter:) withArguments:fighter]; + [cruiser loadFighter:fighter]; }); + + it(@"can match a selector with specific multiple arguments", ^{ + [[cruiser should] receive:@selector(raiseWithName:description:) withArguments:description, format]; + + [cruiser raiseWithName:description description:format]; + }); + + it(@"can match a selector with multiple arguments", ^{ + [[cruiser should] receive:@selector(raiseWithName:description:)]; + + [cruiser raiseWithName:description description:format]; + }); +}); +describe(@"block message patterns", ^{ + id argument1 = [NSObject new]; + id argument2 = [NSObject new]; + + it(@"can match a call with a call without arguments", ^{ + id block = theBlockProxy(^{ }); + + [[block should] beEvaluated]; + + ((KWVoidTestBlock)block)(); + }); + + it(@"can match a call with a specific single argument", ^{ + id block = theBlockProxy(^(id object) { }); + + [[block should] beEvaluatedWithArguments:argument1]; + + ((KWArgumentTestBlock)block)(argument1); + }); + + it(@"can match a call with specific multiple arguments", ^{ + id block = theBlockProxy(^(id object1, id object2, id object3) { }); + + [[block should] beEvaluatedWithArguments:argument1, nil, argument2]; + + ((KWMultiArgumentTestBlock)block)(argument1, nil, argument2); + }); + + it(@"can match a call with multiple arguments", ^{ + id block = theBlockProxy(^(id object1, id object2, id object3) { }); + + [[block should] beEvaluated]; + + ((KWMultiArgumentTestBlock)block)(argument1, nil, argument2); + }); }); SPEC_END diff --git a/Tests/KWProxyBlockTest.m b/Tests/KWProxyBlockTest.m new file mode 100644 index 00000000..52be77e0 --- /dev/null +++ b/Tests/KWProxyBlockTest.m @@ -0,0 +1,173 @@ +// +// KWProxyBlockTest.m +// Kiwi +// +// Created by Oleksa 'trimm' Korin on 4/18/16. +// Copyright © 2016 Allen Ding. All rights reserved. +// + +#import + +#import "KiwiTestConfiguration.h" + +#import "KWProxyBlock.h" + +#if KW_TESTS_ENABLED + + +static const uint8_t KWTestStructValueCount = 4; + +struct KWTestStruct { + uint64_t values[KWTestStructValueCount]; +}; +typedef struct KWTestStruct KWTestStruct; + +static +BOOL KWTestStructEqualsTestStruct(KWTestStruct struct1, KWTestStruct struct2) { + for (uint8_t i = 0; i < KWTestStructValueCount; i++) { + if (struct1.values[i] != struct2.values[i]) { + return NO; + } + } + + return YES; +} + +// test values +static const NSUInteger KWUIntValue = 1; +static NSString * const KWStringValue = @"mama"; +static const KWTestStruct KWStructValue = (KWTestStruct){{1.0, 1.0, 1.0, 1.0}}; + +// test typedefs +typedef void(^__KWVoidBlock)(void); +typedef void(^__KWVoidBlockMultiparam)(NSUInteger, id, KWTestStruct); + +typedef NSUInteger(^__KWPrimitiveBlock)(void); + +typedef id(^__KWObjectBlock)(void); +typedef id(^__KWObjectBlockMultiparam)(NSUInteger, id, KWTestStruct); + +typedef KWTestStruct(^__KWStretBlock)(void); +typedef KWTestStruct(^__KWStretBlockMultiparam)(NSUInteger, id, KWTestStruct); + +@interface KWProxyBlockTest : XCTestCase + +- (void)assertCorrectParamsWithUInteger:(NSUInteger)intValue object:(id)object structure:(KWTestStruct)rect; + +@end + +@implementation KWProxyBlockTest { + __block BOOL _evaluated; +} + +- (void)setUp { + [super setUp]; + + _evaluated = NO; +} + +- (void)tearDown { + XCTAssertTrue(_evaluated, "wrapped block wasn't evaluated"); + + [super tearDown]; +} + +#pragma mark - Test block without parameters + +- (void)testItShouldEvaluateWrappedVoidBlock { + __KWVoidBlock block = ^{ self->_evaluated = YES; }; + + KWProxyBlock *wrappedBlock = [KWProxyBlock blockWithBlock:block]; + + ((__KWVoidBlock)wrappedBlock)(); +} + +- (void)testItShouldEvaluateWrappedPrimitiveBlock { + __KWPrimitiveBlock block = ^{ + self->_evaluated = YES; + + return KWUIntValue; + }; + + KWProxyBlock *wrappedBlock = [KWProxyBlock blockWithBlock:block]; + + XCTAssertEqual(KWUIntValue, ((__KWPrimitiveBlock)wrappedBlock)(), "wrapped block didn't return a proper value"); +} + +- (void)testItShouldEvaluateWrappedObjectBlock { + __KWObjectBlock block = ^{ + self->_evaluated = YES; + + return KWStringValue; + }; + + KWProxyBlock *wrappedBlock = [KWProxyBlock blockWithBlock:block]; + + XCTAssertEqualObjects(KWStringValue, ((__KWObjectBlock)wrappedBlock)(), "wrapped block didn't return a proper value"); +} + +- (void)testItShouldEvaluateWrappedStretBlock { + __KWStretBlock block = ^{ + self->_evaluated = YES; + + return KWStructValue; + }; + + KWProxyBlock *wrappedBlock = [KWProxyBlock blockWithBlock:block]; + + XCTAssertTrue(KWTestStructEqualsTestStruct(KWStructValue, ((__KWStretBlock)wrappedBlock)()), + "wrapped block didn't return a proper value"); +} + +#pragma mark - Test block with parameters + +- (void)testItShouldEvaluateWrappedVoidBlockWithMultipleParameters { + __KWVoidBlockMultiparam block = ^(NSUInteger intValue, id object, KWTestStruct rect) { + [self assertCorrectParamsWithUInteger:intValue object:object structure:rect]; + }; + + KWProxyBlock *wrappedBlock = [KWProxyBlock blockWithBlock:block]; + + ((__KWVoidBlockMultiparam)wrappedBlock)(KWUIntValue, KWStringValue, KWStructValue); +} + +- (void)testItShouldEvaluateWrappedObjectBlockWithMultipleParameters { + __KWObjectBlockMultiparam block = ^(NSUInteger intValue, id object, KWTestStruct rect) { + [self assertCorrectParamsWithUInteger:intValue object:object structure:rect]; + + return KWStringValue; + }; + + KWProxyBlock *wrappedBlock = [KWProxyBlock blockWithBlock:block]; + + id object = ((__KWObjectBlockMultiparam)wrappedBlock)(KWUIntValue, KWStringValue, KWStructValue); + + XCTAssertEqualObjects(KWStringValue, object, "wrapped block didn't return a proper value"); +} + +- (void)testItShouldEvaluateWrappedStretBlockWithMultipleParameters { + __KWStretBlockMultiparam block = ^(NSUInteger intValue, id object, KWTestStruct rect) { + [self assertCorrectParamsWithUInteger:intValue object:object structure:rect]; + + return KWStructValue; + }; + + KWProxyBlock *wrappedBlock = [KWProxyBlock blockWithBlock:block]; + + KWTestStruct rect = ((__KWStretBlockMultiparam)wrappedBlock)(KWUIntValue, KWStringValue, KWStructValue); + + XCTAssertTrue(KWTestStructEqualsTestStruct(KWStructValue, rect), "wrapped block didn't return a proper value"); +} + + +- (void)assertCorrectParamsWithUInteger:(NSUInteger)intValue object:(id)object structure:(KWTestStruct)rect { + _evaluated = YES; + + XCTAssertTrue(KWTestStructEqualsTestStruct(KWStructValue, rect), "wrapped block didn't return a proper value"); + XCTAssertEqualObjects(KWStringValue, object, "wrapped block didn't return a proper value"); + XCTAssertEqual(KWUIntValue, intValue, "wrapped block didn't return a proper value"); +} + +@end + +#endif