diff --git a/Source/OCMock/OCMInvocationMatcher.m b/Source/OCMock/OCMInvocationMatcher.m index 247f51d8..540d86ca 100644 --- a/Source/OCMock/OCMInvocationMatcher.m +++ b/Source/OCMock/OCMInvocationMatcher.m @@ -45,6 +45,7 @@ - (void)setInvocation:(NSInvocation *)anInvocation // anInvocation contains self as an argument, -retainArguments would create a retain cycle. [anInvocation retainObjectArgumentsExcludingObject:self]; recordedInvocation = [anInvocation retain]; + [self verifyInvocationCompatibleWithIgnoreNonObjectArgs]; } - (void)setRecordedAsClassMethod:(BOOL)flag @@ -60,6 +61,7 @@ - (BOOL)recordedAsClassMethod - (void)setIgnoreNonObjectArgs:(BOOL)flag { ignoreNonObjectArgs = flag; + [self verifyInvocationCompatibleWithIgnoreNonObjectArgs]; } - (NSString *)description @@ -142,4 +144,27 @@ - (BOOL)matchesInvocation:(NSInvocation *)anInvocation return YES; } +- (void)verifyInvocationCompatibleWithIgnoreNonObjectArgs +{ + if (!recordedInvocation || !ignoreNonObjectArgs) + { + return; + } + NSMethodSignature *signature = [recordedInvocation methodSignature]; + NSUInteger n = [signature numberOfArguments]; + BOOL foundNonObjectArg = NO; + for(NSUInteger i = 2; i < n; i++) + { + if(!OCMIsObjectType([signature getArgumentTypeAtIndex:i])) + { + foundNonObjectArg = YES; + break; + } + } + if (!foundNonObjectArg) + { + [NSException raise:NSInvalidArgumentException format:@"Method `%@` with %@ marked as ignoreNonObjectArgs.", NSStringFromSelector([recordedInvocation selector]), n == 2 ? @"0 args" : @"only object args"]; + } +} + @end diff --git a/Source/OCMockTests/OCMockObjectTests.m b/Source/OCMockTests/OCMockObjectTests.m index 4d33bfd6..01493c0a 100644 --- a/Source/OCMockTests/OCMockObjectTests.m +++ b/Source/OCMockTests/OCMockObjectTests.m @@ -465,9 +465,10 @@ - (void)testRaisesExceptionWhenMethodWithMixedArgumentsIsCalledWithWrongObjectAr - (void)testBlocksAreNotConsideredNonObjectArguments { - [[[mock stub] ignoringNonObjectArgs] enumerateLinesUsingBlock:[OCMArg invokeBlock]]; + mock = [OCMockObject mockForClass:[NSArray class]]; + [[[mock stub] ignoringNonObjectArgs] enumerateObjectsWithOptions:0 usingBlock:[OCMArg invokeBlock]]; __block BOOL blockWasInvoked = NO; - [mock enumerateLinesUsingBlock:^(NSString * _Nonnull line, BOOL * _Nonnull stop) { + [mock enumerateObjectsWithOptions:5 usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { blockWasInvoked = YES; }]; XCTAssertTrue(blockWasInvoked, @"Should not have ignored the block argument."); @@ -479,6 +480,29 @@ - (void)testThrowsWhenAttemptingToStubMethodOnStoppedMock XCTAssertThrowsSpecificNamed([[mock stub] rangeOfString:@"foo" options:0], NSException, NSInternalInconsistencyException); } +- (void)testRaisesExceptionWhenIgnoringNonObjectArgumentsOnMethodThatHasZeroArgs +{ + @try + { + [[[mock stub] ignoringNonObjectArgs] uppercaseString]; + } + @catch (NSException *e) + { + XCTAssertTrue([[e reason] containsString:@"0 args"]); + } +} + +- (void)testRaisesExceptionWhenIgnoringNonObjectArgumentsOnMethodThatOnlyTakesObjects +{ + @try + { + [[[mock stub] ignoringNonObjectArgs] stringByAppendingString:[OCMArg any]]; + } + @catch (NSException *e) + { + XCTAssertTrue([[e reason] containsString:@"only object args"]); + } +} #pragma mark returning values from stubbed methods