Skip to content

Instantly share code, notes, and snippets.

@lukeredpath
Created July 27, 2010 14:57
Show Gist options
  • Select an option

  • Save lukeredpath/492336 to your computer and use it in GitHub Desktop.

Select an option

Save lukeredpath/492336 to your computer and use it in GitHub Desktop.
- (BOOL)calledWithCorrectParameters:(NSInvocation *)invocation
{
NSMethodSignature *methodSignature = [invocation methodSignature];
BOOL parametersAreCorrect = YES;
for (int i = 2; i < [methodSignature numberOfArguments]; i++) {
const char *argType = [methodSignature getArgumentTypeAtIndex:i];
if (*argType == *@encode(id)) {
id receivedArg;
id expectedArg;
[invocation getArgument:&receivedArg atIndex:i];
[expectedInvocation getArgument:&expectedArg atIndex:i];
parametersAreCorrect = [receivedArg isEqual:expectedArg];
}
else if(*argType == *@encode(int))
{
int receivedArg;
int expectedArg;
[invocation getArgument:&receivedArg atIndex:i];
[expectedInvocation getArgument:&expectedArg atIndex:i];
parametersAreCorrect = (receivedArg == expectedArg);
}
else
{
NSLog(@"Unknown argType %c", *argType);
parametersAreCorrect = NO;
}
}
return parametersAreCorrect;
}
@implementation LRInvocationComparitor
+ (id)comparitorForInvocation:(NSInvocation *)invocation;
{
return [[[self alloc] initWithInvocation:invocation] autorelease];
}
- (id)initWithInvocation:(NSInvocation *)anInvocation;
{
if (self = [super init]) {
expectedInvocation = [anInvocation retain];
[expectedInvocation retainArguments];
}
return self;
}
- (void)dealloc
{
[expectedInvocation release];
[super dealloc];
}
- (BOOL)matchesParameters:(NSInvocation *)invocation;
{
NSMethodSignature *methodSignature = [expectedInvocation methodSignature];
BOOL matchesParameters = YES;
for (int i = 2; i < [methodSignature numberOfArguments]; i++) {
const char *argType = [methodSignature getArgumentTypeAtIndex:i];
void *receivedArg;
void *expectedArg;
[invocation getArgument:&receivedArg atIndex:i];
[expectedInvocation getArgument:&expectedArg atIndex:i];
if (*argType == *@encode(id)) {
matchesParameters = [(id)receivedArg isEqual:(id)expectedArg];
}
else
{
matchesParameters = (receivedArg == expectedArg);
}
}
return matchesParameters;
}
@end
@implementation LRInvocationComparitorTest
- (void)setUp;
{
testObject = [[InvocationTesterObject alloc] init];
capture = [[InvocationCapturer alloc] init];
}
- (void)testCanCompareObjectMethodParameters
{
NSInvocation *expected = [[capture takesAnObject:@"foo"] invocation];
LRInvocationComparitor *comparitor = [LRInvocationComparitor comparitorForInvocation:expected];
assertThatBool([comparitor matchesParameters:[[capture takesAnObject:@"foo"] invocation]],
equalToBool(YES));
assertThatBool([comparitor matchesParameters:[[capture takesAnObject:@"bar"] invocation]],
equalToBool(NO));
}
- (void)testCanCompareIntegerParameters
{
NSInvocation *expected = [[capture takesAnInt:10] invocation];
LRInvocationComparitor *comparitor = [LRInvocationComparitor comparitorForInvocation:expected];
assertThatBool([comparitor matchesParameters:[[capture takesAnInt:10] invocation]],
equalToBool(YES));
assertThatBool([comparitor matchesParameters:[[capture takesAnInt:20] invocation]],
equalToBool(NO));
}
- (void)testCanCompareFloatParameters
{
NSInvocation *expected = [[capture takesAFloat:3.14] invocation];
LRInvocationComparitor *comparitor = [LRInvocationComparitor comparitorForInvocation:expected];
assertThatBool([comparitor matchesParameters:[[capture takesAFloat:3.14] invocation]],
equalToBool(YES));
assertThatBool([comparitor matchesParameters:[[capture takesAFloat:10.45] invocation]],
equalToBool(NO));
}
@end
@lukeredpath
Copy link
Copy Markdown
Author

You should be able to set expectations with parameter constraints that are objects or non-objects. The above code supports object types and integers but isn't going to scale very well; I don't particularly want to do a massive if statement over the different arg types and even if I could refactor it into something polymorphic it still seems like there should be a more generic way of handling this. Any ideas? I'm well aware of the limits in my C and Obj-C runtime knowledge.

@lukeredpath
Copy link
Copy Markdown
Author

Top file shows a potential solution.

@floehopper
Copy link
Copy Markdown

Don't you need to reserve some memory for receivedArg and expectedArg?

@floehopper
Copy link
Copy Markdown

@lukeredpath
Copy link
Copy Markdown
Author

That doesn't seem to be an issue; it works as is.

@floehopper
Copy link
Copy Markdown

Do you know whether it is actually allocating memory somehow? Otherwise, there's a good chance this will stop working at an inconvenient moment ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment