Skip to content

Commit

Permalink
Add location recording to all macros.
Browse files Browse the repository at this point in the history
For large CI systems it can be costly to generate debug information.
Recording location data for all of the stub/expect/reject macros makes
it a lot easier to record/find failures.
  • Loading branch information
dmaclach committed May 29, 2020
1 parent 9b0e314 commit 84689fa
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 22 deletions.
6 changes: 3 additions & 3 deletions Source/OCMock/OCMMacroState.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@
BOOL invocationDidThrow;
}

+ (void)beginStubMacro;
+ (void)beginStubMacroAtLocation:(OCMLocation *)aLocation;
+ (OCMStubRecorder *)endStubMacro;

+ (void)beginExpectMacro;
+ (void)beginExpectMacroAtLocation:(OCMLocation *)aLocation;
+ (OCMStubRecorder *)endExpectMacro;

+ (void)beginRejectMacro;
+ (void)beginRejectMacroAtLocation:(OCMLocation *)aLocation;
+ (OCMStubRecorder *)endRejectMacro;

+ (void)beginVerifyMacroAtLocation:(OCMLocation *)aLocation;
Expand Down
36 changes: 24 additions & 12 deletions Source/OCMock/OCMMacroState.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ @implementation OCMMacroState

#pragma mark Methods to begin/end macros

+ (void)beginStubMacro
+ (void)beginStubMacroAtLocation:(OCMLocation *)aLocation
{
OCMStubRecorder *recorder = [[[OCMStubRecorder alloc] init] autorelease];
recorder.ocm_location = aLocation;
OCMMacroState *macroState = [[OCMMacroState alloc] initWithRecorder:recorder];
[NSThread currentThread].threadDictionary[OCMGlobalStateKey] = macroState;
[macroState release];
Expand All @@ -40,21 +41,31 @@ + (OCMStubRecorder *)endStubMacro
OCMStubRecorder *recorder = [[(OCMStubRecorder *)[globalState recorder] retain] autorelease];
BOOL didThrow = [globalState invocationDidThrow];
[threadDictionary removeObjectForKey:OCMGlobalStateKey];
if(didThrow == NO && [recorder wasUsed] == NO)
{
[NSException raise:NSInternalInconsistencyException
format:@"Did not record an invocation in OCMStub/OCMExpect/OCMReject.\n"
@"Possible causes are:\n"
@"- The receiver is not a mock object.\n"
@"- The selector conflicts with a selector implemented by OCMStubRecorder/OCMExpectationRecorder."];
}
if(didThrow == NO && [recorder wasUsed] == NO)
{
OCMLocation *location = recorder.ocm_location;
NSString *explanation = @"Did not record an invocation in OCMStub/OCMExpect/OCMReject.\n"
@"Possible causes are:\n"
@"- The receiver is not a mock object.\n"
@"- The selector conflicts with a selector implemented by OCMStubRecorder/OCMExpectationRecorder.";
if(location != nil)
{
[NSException raise:NSInternalInconsistencyException
format:@"%@:%d :%@", [location file], (int)[location line], explanation];
}
else
{
[NSException raise:NSInternalInconsistencyException format:@"%@", explanation];
}
}
return recorder;
}


+ (void)beginExpectMacro
+ (void)beginExpectMacroAtLocation:(OCMLocation *)aLocation
{
OCMExpectationRecorder *recorder = [[[OCMExpectationRecorder alloc] init] autorelease];
recorder.ocm_location = aLocation;
OCMMacroState *macroState = [[OCMMacroState alloc] initWithRecorder:recorder];
[NSThread currentThread].threadDictionary[OCMGlobalStateKey] = macroState;
[macroState release];
Expand All @@ -66,9 +77,10 @@ + (OCMStubRecorder *)endExpectMacro
}


+ (void)beginRejectMacro
+ (void)beginRejectMacroAtLocation:(OCMLocation *)aLocation
{
OCMExpectationRecorder *recorder = [[[OCMExpectationRecorder alloc] init] autorelease];
recorder.ocm_location = aLocation;
OCMMacroState *macroState = [[OCMMacroState alloc] initWithRecorder:recorder];
[NSThread currentThread].threadDictionary[OCMGlobalStateKey] = macroState;
[macroState release];
Expand All @@ -92,7 +104,7 @@ + (void)beginVerifyMacroAtLocation:(OCMLocation *)aLocation
+ (void)beginVerifyMacroAtLocation:(OCMLocation *)aLocation withQuantifier:(OCMQuantifier *)quantifier
{
OCMVerifier *recorder = [[[OCMVerifier alloc] init] autorelease];
[recorder setLocation:aLocation];
recorder.ocm_location = aLocation;
[recorder setQuantifier:quantifier];
OCMMacroState *macroState = [[OCMMacroState alloc] initWithRecorder:recorder];
[NSThread currentThread].threadDictionary[OCMGlobalStateKey] = macroState;
Expand Down
4 changes: 4 additions & 0 deletions Source/OCMock/OCMRecorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#import <Foundation/Foundation.h>

#import "OCMLocation.h"

@class OCMockObject;
@class OCMInvocationMatcher;

Expand All @@ -28,6 +30,8 @@
BOOL shouldReturnMockFromInit;
}

// Using `ocm_` prefix to minimize clashes with mocked objects using `location` as a property.
@property(retain) OCMLocation *ocm_location;

- (instancetype)init;
- (instancetype)initWithMockObject:(OCMockObject *)aMockObject;
Expand Down
1 change: 1 addition & 0 deletions Source/OCMock/OCMRecorder.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ - (void)setShouldReturnMockFromInit:(BOOL)flag

- (void)dealloc
{
[_ocm_location release];
[invocationMatcher release];
[super dealloc];
}
Expand Down
2 changes: 0 additions & 2 deletions Source/OCMock/OCMVerifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@
*/

#import "OCMRecorder.h"
#import "OCMLocation.h"
#import "OCMQuantifier.h"


@interface OCMVerifier : OCMRecorder

@property(retain) OCMLocation *location;
@property(retain) OCMQuantifier *quantifier;

- (instancetype)withQuantifier:(OCMQuantifier *)quantifier;
Expand Down
3 changes: 1 addition & 2 deletions Source/OCMock/OCMVerifier.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,11 @@ - (instancetype)withQuantifier:(OCMQuantifier *)quantifier
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[super forwardInvocation:anInvocation];
[mockObject verifyInvocation:invocationMatcher withQuantifier:self.quantifier atLocation:self.location];
[mockObject verifyInvocation:invocationMatcher withQuantifier:self.quantifier atLocation:self.ocm_location];
}

- (void)dealloc
{
[_location release];
[_quantifier release];
[super dealloc];
}
Expand Down
6 changes: 3 additions & 3 deletions Source/OCMock/OCMock.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
#define OCMStub(invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginStubMacro]; \
[OCMMacroState beginStubMacroAtLocation:OCMMakeLocation(self, __FILE__, __LINE__)]; \
OCMStubRecorder *recorder = nil; \
@try{ \
invocation; \
Expand All @@ -65,7 +65,7 @@
#define OCMExpect(invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginExpectMacro]; \
[OCMMacroState beginExpectMacroAtLocation:OCMMakeLocation(self, __FILE__, __LINE__)]; \
OCMStubRecorder *recorder = nil; \
@try{ \
invocation; \
Expand All @@ -82,7 +82,7 @@
#define OCMReject(invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginRejectMacro]; \
[OCMMacroState beginRejectMacroAtLocation:OCMMakeLocation(self, __FILE__, __LINE__)]; \
OCMStubRecorder *recorder = nil; \
@try{ \
invocation; \
Expand Down
48 changes: 48 additions & 0 deletions Source/OCMockTests/OCMockObjectMacroTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,54 @@ - (void)testShouldHintAtPossibleReasonWhenVerifyingMethodThatCannotBeMocked
}
}

- (void)testStubExpectAndRejectShouldCaptureFileAndLineNumbers
{
NSString *expectedFile = [NSString stringWithUTF8String:__FILE__];
int expectedLine;
BOOL caughtException = NO;
id realObject = [NSMutableArray array];

@try
{
caughtException = NO;
expectedLine = __LINE__; OCMStub([realObject addObject:@"foo"]);
}
@catch (NSException *e)
{
XCTAssertTrue([[e reason] containsString:expectedFile], @"`%@` should contain `%@`", [e reason], expectedFile);
XCTAssertTrue([[e reason] containsString:[[NSNumber numberWithInt:expectedLine] stringValue]], @"`%@` should contain `%d`", [e reason], expectedLine);
caughtException = YES;
}
XCTAssertTrue(caughtException);

@try
{
caughtException = NO;
expectedLine = __LINE__; OCMExpect([realObject addObject:@"foo"]);
}
@catch (NSException *e)
{
XCTAssertTrue([[e reason] containsString:expectedFile], @"`%@` should contain `%@`", [e reason], expectedFile);
XCTAssertTrue([[e reason] containsString:[[NSNumber numberWithInt:expectedLine] stringValue]], @"`%@` should contain `%d`", [e reason], expectedLine);
caughtException = YES;
}
XCTAssertTrue(caughtException);

@try
{
caughtException = NO;
expectedLine = __LINE__; OCMReject([realObject addObject:@"foo"]);
}
@catch (NSException *e)
{
XCTAssertTrue([[e reason] containsString:expectedFile], @"`%@` should contain `%@`", [e reason], expectedFile);
XCTAssertTrue([[e reason] containsString:[[NSNumber numberWithInt:expectedLine] stringValue]], @"`%@` should contain `%d`", [e reason], expectedLine);
caughtException = YES;
}
XCTAssertTrue(caughtException);

}


- (void)testCanExplicitlySelectClassMethodForStubs
{
Expand Down

0 comments on commit 84689fa

Please sign in to comment.