Skip to content

Commit 096e69a

Browse files
committed
Make OCMInvocationConstraint retain invocation
1 parent 0d83118 commit 096e69a

File tree

3 files changed

+104
-17
lines changed

3 files changed

+104
-17
lines changed

Source/OCMock/OCMConstraint.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,12 @@
5959

6060
@interface OCMInvocationConstraint : OCMConstraint
6161
{
62-
@public
63-
NSInvocation *invocation;
62+
NSInvocation *invocation;
6463
}
6564

65+
- (instancetype)initWithInvocation:(NSInvocation *)invocation NS_DESIGNATED_INITIALIZER;
66+
- (instancetype)init NS_UNAVAILABLE;
67+
6668
@end
6769

6870
@interface OCMBlockConstraint : OCMConstraint

Source/OCMock/OCMConstraint.m

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
#import <OCMock/OCMConstraint.h>
18-
18+
#import "OCMFunctions.h"
1919

2020
@implementation OCMConstraint
2121

@@ -34,26 +34,30 @@ - (id)copyWithZone:(struct _NSZone *)zone
3434
return [self retain];
3535
}
3636

37+
+ (NSInvocation *)invocationWithSelector:(SEL)aSelector onObject:(id)anObject
38+
{
39+
NSMethodSignature *signature = [anObject methodSignatureForSelector:aSelector];
40+
if(signature == nil)
41+
[NSException raise:NSInvalidArgumentException format:@"Unknown selector %@ used in constraint.", NSStringFromSelector(aSelector)];
42+
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
43+
[invocation setTarget:anObject];
44+
[invocation setSelector:aSelector];
45+
return invocation;
46+
}
47+
3748
+ (instancetype)constraintWithSelector:(SEL)aSelector onObject:(id)anObject
3849
{
39-
OCMInvocationConstraint *constraint = [OCMInvocationConstraint constraint];
40-
NSMethodSignature *signature = [anObject methodSignatureForSelector:aSelector];
41-
if(signature == nil)
42-
[NSException raise:NSInvalidArgumentException format:@"Unkown selector %@ used in constraint.", NSStringFromSelector(aSelector)];
43-
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
44-
[invocation setTarget:anObject];
45-
[invocation setSelector:aSelector];
46-
constraint->invocation = invocation;
47-
return constraint;
50+
NSInvocation *invocation = [self invocationWithSelector:aSelector onObject:anObject];
51+
return [[[OCMInvocationConstraint alloc] initWithInvocation:invocation] autorelease];
4852
}
4953

5054
+ (instancetype)constraintWithSelector:(SEL)aSelector onObject:(id)anObject withValue:(id)aValue
5155
{
52-
OCMInvocationConstraint *constraint = (OCMInvocationConstraint *)[self constraintWithSelector:aSelector onObject:anObject];
53-
if([[constraint->invocation methodSignature] numberOfArguments] < 4)
54-
[NSException raise:NSInvalidArgumentException format:@"Constraint with value requires selector with two arguments."];
55-
[constraint->invocation setArgument:&aValue atIndex:3];
56-
return constraint;
56+
NSInvocation *invocation = [self invocationWithSelector:aSelector onObject:anObject];
57+
if([[invocation methodSignature] numberOfArguments] < 4)
58+
[NSException raise:NSInvalidArgumentException format:@"Constraint with value requires selector with two arguments."];
59+
[invocation setArgument:&aValue atIndex:3];
60+
return [[[OCMInvocationConstraint alloc] initWithInvocation:invocation] autorelease];
5761
}
5862

5963

@@ -153,6 +157,33 @@ - (BOOL)evaluate:(id)value
153157

154158
@implementation OCMInvocationConstraint
155159

160+
- (instancetype)initWithInvocation:(NSInvocation *)anInvocation {
161+
if((self = [super init]))
162+
{
163+
NSMethodSignature *signature = [anInvocation methodSignature];
164+
if([signature numberOfArguments] < 3)
165+
{
166+
[NSException raise:NSInvalidArgumentException format:@"invocation must take at least one argument (other than _cmd and self)"];
167+
}
168+
if(!(OCMIsObjectType([signature getArgumentTypeAtIndex:2])))
169+
{
170+
[NSException raise:NSInvalidArgumentException format:@"invocation's second argument must be an object type"];
171+
}
172+
if(strcmp([signature methodReturnType], @encode(BOOL)))
173+
{
174+
[NSException raise:NSInvalidArgumentException format:@"invocation must return BOOL"];
175+
}
176+
invocation = [anInvocation retain];
177+
}
178+
return self;
179+
}
180+
181+
- (void)dealloc
182+
{
183+
[invocation release];
184+
[super dealloc];
185+
}
186+
156187
- (BOOL)evaluate:(id)value
157188
{
158189
[invocation setArgument:&value atIndex:2]; // should test if constraint takes arg

Source/OCMockTests/OCMConstraintTests.m

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,4 +205,58 @@ - (void)testEvaluateNilBlockReturnsNo
205205
XCTAssertFalse([constraint evaluate:@"foo"]);
206206
}
207207

208+
- (void)testEvaluateInvocationRetainsInvocation
209+
{
210+
OCMInvocationConstraint *constraint;
211+
@autoreleasepool {
212+
SEL selector = @selector(checkArg:);
213+
NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]];
214+
[anInvocation setTarget:self];
215+
[anInvocation setSelector:selector];
216+
constraint = [[OCMInvocationConstraint alloc] initWithInvocation:anInvocation];
217+
}
218+
XCTAssertTrue([constraint evaluate:@"foo"]);
219+
}
220+
221+
- (BOOL)methodWithNoArgs
222+
{
223+
return YES;
224+
}
225+
226+
- (void)testEvaluateInvocationThrowsForInvocationForMethodWithoutArgument
227+
{
228+
SEL selector = @selector(methodWithNoArgs);
229+
NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]];
230+
[anInvocation setTarget:self];
231+
[anInvocation setSelector:selector];
232+
XCTAssertThrowsSpecificNamed([[OCMInvocationConstraint alloc] initWithInvocation:anInvocation], NSException, NSInvalidArgumentException);
233+
}
234+
235+
- (BOOL)aMethodWithInt:(int)anInt
236+
{
237+
return YES;
238+
}
239+
240+
- (void)testEvaluateInvocationThrowsForInvocationForMethodWithoutObjectArgument
241+
{
242+
SEL selector = @selector(aMethodWithInt:);
243+
NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]];
244+
[anInvocation setTarget:self];
245+
[anInvocation setSelector:selector];
246+
XCTAssertThrowsSpecificNamed([[OCMInvocationConstraint alloc] initWithInvocation:anInvocation], NSException, NSInvalidArgumentException);
247+
}
248+
249+
- (void)aMethodThatDoesNotReturnBool:(id)anArg
250+
{
251+
}
252+
253+
- (void)testEvaluateInvocationThrowsForInvocationThatDoesNotReturnBool
254+
{
255+
SEL selector = @selector(aMethodThatDoesNotReturnBool:);
256+
NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]];
257+
[anInvocation setTarget:self];
258+
[anInvocation setSelector:selector];
259+
XCTAssertThrowsSpecificNamed([[OCMInvocationConstraint alloc] initWithInvocation:anInvocation], NSException, NSInvalidArgumentException);
260+
}
261+
208262
@end

0 commit comments

Comments
 (0)