Skip to content

Commit 87b4cb6

Browse files
committed
Add location recording to all macros.
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.
1 parent bd3aa2e commit 87b4cb6

8 files changed

+85
-23
lines changed

Source/OCMock/OCMMacroState.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@
2929
BOOL invocationDidThrow;
3030
}
3131

32-
+ (void)beginStubMacro;
32+
+ (void)beginStubMacroAtLocation:(OCMLocation *)aLocation;
3333
+ (OCMStubRecorder *)endStubMacro;
3434

35-
+ (void)beginExpectMacro;
35+
+ (void)beginExpectMacroAtLocation:(OCMLocation *)aLocation;
3636
+ (OCMStubRecorder *)endExpectMacro;
3737

38-
+ (void)beginRejectMacro;
38+
+ (void)beginRejectMacroAtLocation:(OCMLocation *)aLocation;
3939
+ (OCMStubRecorder *)endRejectMacro;
4040

4141
+ (void)beginVerifyMacroAtLocation:(OCMLocation *)aLocation;

Source/OCMock/OCMMacroState.m

+24-12
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ @implementation OCMMacroState
2525

2626
#pragma mark Methods to begin/end macros
2727

28-
+ (void)beginStubMacro
28+
+ (void)beginStubMacroAtLocation:(OCMLocation *)aLocation
2929
{
3030
OCMStubRecorder *recorder = [[[OCMStubRecorder alloc] init] autorelease];
31+
recorder.ocm_location = aLocation;
3132
OCMMacroState *macroState = [[OCMMacroState alloc] initWithRecorder:recorder];
3233
[NSThread currentThread].threadDictionary[OCMGlobalStateKey] = macroState;
3334
[macroState release];
@@ -40,21 +41,31 @@ + (OCMStubRecorder *)endStubMacro
4041
OCMStubRecorder *recorder = [[(OCMStubRecorder *)[globalState recorder] retain] autorelease];
4142
BOOL didThrow = [globalState invocationDidThrow];
4243
[threadDictionary removeObjectForKey:OCMGlobalStateKey];
43-
if(didThrow == NO && [recorder wasUsed] == NO)
44-
{
45-
[NSException raise:NSInternalInconsistencyException
46-
format:@"Did not record an invocation in OCMStub/OCMExpect/OCMReject.\n"
47-
@"Possible causes are:\n"
48-
@"- The receiver is not a mock object.\n"
49-
@"- The selector conflicts with a selector implemented by OCMStubRecorder/OCMExpectationRecorder."];
50-
}
44+
if(didThrow == NO && [recorder wasUsed] == NO)
45+
{
46+
OCMLocation *location = recorder.ocm_location;
47+
NSString *explanation = @"Did not record an invocation in OCMStub/OCMExpect/OCMReject.\n"
48+
@"Possible causes are:\n"
49+
@"- The receiver is not a mock object.\n"
50+
@"- The selector conflicts with a selector implemented by OCMStubRecorder/OCMExpectationRecorder.";
51+
if(location != nil)
52+
{
53+
[NSException raise:NSInternalInconsistencyException
54+
format:@"%@:%d :%@", [location file], (int)[location line], explanation];
55+
}
56+
else
57+
{
58+
[NSException raise:NSInternalInconsistencyException format:@"%@", explanation];
59+
}
60+
}
5161
return recorder;
5262
}
5363

5464

55-
+ (void)beginExpectMacro
65+
+ (void)beginExpectMacroAtLocation:(OCMLocation *)aLocation
5666
{
5767
OCMExpectationRecorder *recorder = [[[OCMExpectationRecorder alloc] init] autorelease];
68+
recorder.ocm_location = aLocation;
5869
OCMMacroState *macroState = [[OCMMacroState alloc] initWithRecorder:recorder];
5970
[NSThread currentThread].threadDictionary[OCMGlobalStateKey] = macroState;
6071
[macroState release];
@@ -66,9 +77,10 @@ + (OCMStubRecorder *)endExpectMacro
6677
}
6778

6879

69-
+ (void)beginRejectMacro
80+
+ (void)beginRejectMacroAtLocation:(OCMLocation *)aLocation
7081
{
7182
OCMExpectationRecorder *recorder = [[[OCMExpectationRecorder alloc] init] autorelease];
83+
recorder.ocm_location = aLocation;
7284
OCMMacroState *macroState = [[OCMMacroState alloc] initWithRecorder:recorder];
7385
[NSThread currentThread].threadDictionary[OCMGlobalStateKey] = macroState;
7486
[macroState release];
@@ -92,7 +104,7 @@ + (void)beginVerifyMacroAtLocation:(OCMLocation *)aLocation
92104
+ (void)beginVerifyMacroAtLocation:(OCMLocation *)aLocation withQuantifier:(OCMQuantifier *)quantifier
93105
{
94106
OCMVerifier *recorder = [[[OCMVerifier alloc] init] autorelease];
95-
[recorder setLocation:aLocation];
107+
recorder.ocm_location = aLocation;
96108
[recorder setQuantifier:quantifier];
97109
OCMMacroState *macroState = [[OCMMacroState alloc] initWithRecorder:recorder];
98110
[NSThread currentThread].threadDictionary[OCMGlobalStateKey] = macroState;

Source/OCMock/OCMRecorder.h

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#import <Foundation/Foundation.h>
1818

19+
#import "OCMLocation.h"
20+
1921
@class OCMockObject;
2022
@class OCMInvocationMatcher;
2123

@@ -28,6 +30,8 @@
2830
BOOL shouldReturnMockFromInit;
2931
}
3032

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

3236
- (instancetype)init;
3337
- (instancetype)initWithMockObject:(OCMockObject *)aMockObject;

Source/OCMock/OCMRecorder.m

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ - (void)setShouldReturnMockFromInit:(BOOL)flag
5151

5252
- (void)dealloc
5353
{
54+
[_ocm_location release];
5455
[invocationMatcher release];
5556
[super dealloc];
5657
}

Source/OCMock/OCMVerifier.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,11 @@
1616

1717
#import "OCMRecorder.h"
1818

19-
@class OCMLocation;
2019
@class OCMQuantifier;
2120

2221
@interface OCMVerifier : OCMRecorder
2322

24-
@property(strong) OCMLocation *location;
25-
@property(strong) OCMQuantifier *quantifier;
23+
@property(retain) OCMQuantifier *quantifier;
2624

2725
- (instancetype)withQuantifier:(OCMQuantifier *)quantifier;
2826

Source/OCMock/OCMVerifier.m

+1-2
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,11 @@ - (instancetype)withQuantifier:(OCMQuantifier *)quantifier
4444
- (void)forwardInvocation:(NSInvocation *)anInvocation
4545
{
4646
[super forwardInvocation:anInvocation];
47-
[mockObject verifyInvocation:invocationMatcher withQuantifier:self.quantifier atLocation:self.location];
47+
[mockObject verifyInvocation:invocationMatcher withQuantifier:self.quantifier atLocation:self.ocm_location];
4848
}
4949

5050
- (void)dealloc
5151
{
52-
[_location release];
5352
[_quantifier release];
5453
[super dealloc];
5554
}

Source/OCMock/OCMock.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
#define OCMStub(invocation) \
4949
({ \
5050
_OCMSilenceWarnings( \
51-
[OCMMacroState beginStubMacro]; \
51+
[OCMMacroState beginStubMacroAtLocation:OCMMakeLocation(nil, __FILE__, __LINE__)]; \
5252
OCMStubRecorder *recorder = nil; \
5353
@try{ \
5454
invocation; \
@@ -65,7 +65,7 @@
6565
#define OCMExpect(invocation) \
6666
({ \
6767
_OCMSilenceWarnings( \
68-
[OCMMacroState beginExpectMacro]; \
68+
[OCMMacroState beginExpectMacroAtLocation:OCMMakeLocation(nil, __FILE__, __LINE__)]; \
6969
OCMStubRecorder *recorder = nil; \
7070
@try{ \
7171
invocation; \
@@ -82,7 +82,7 @@
8282
#define OCMReject(invocation) \
8383
({ \
8484
_OCMSilenceWarnings( \
85-
[OCMMacroState beginRejectMacro]; \
85+
[OCMMacroState beginRejectMacroAtLocation:OCMMakeLocation(nil, __FILE__, __LINE__)]; \
8686
OCMStubRecorder *recorder = nil; \
8787
@try{ \
8888
invocation; \

Source/OCMockTests/OCMockObjectMacroTests.m

+48
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,54 @@ - (void)testShouldHintAtPossibleReasonWhenVerifyingMethodThatCannotBeMocked
390390
}
391391
}
392392

393+
- (void)testStubExpectAndRejectShouldCaptureFileAndLineNumbers
394+
{
395+
NSString *expectedFile = [NSString stringWithUTF8String:__FILE__];
396+
int expectedLine;
397+
BOOL caughtException = NO;
398+
id realObject = [NSMutableArray array];
399+
400+
@try
401+
{
402+
caughtException = NO;
403+
expectedLine = __LINE__; OCMStub([realObject addObject:@"foo"]);
404+
}
405+
@catch (NSException *e)
406+
{
407+
XCTAssertTrue([[e reason] containsString:expectedFile], @"`%@` should contain `%@`", [e reason], expectedFile);
408+
XCTAssertTrue([[e reason] containsString:[[NSNumber numberWithInt:expectedLine] stringValue]], @"`%@` should contain `%d`", [e reason], expectedLine);
409+
caughtException = YES;
410+
}
411+
XCTAssertTrue(caughtException);
412+
413+
@try
414+
{
415+
caughtException = NO;
416+
expectedLine = __LINE__; OCMExpect([realObject addObject:@"foo"]);
417+
}
418+
@catch (NSException *e)
419+
{
420+
XCTAssertTrue([[e reason] containsString:expectedFile], @"`%@` should contain `%@`", [e reason], expectedFile);
421+
XCTAssertTrue([[e reason] containsString:[[NSNumber numberWithInt:expectedLine] stringValue]], @"`%@` should contain `%d`", [e reason], expectedLine);
422+
caughtException = YES;
423+
}
424+
XCTAssertTrue(caughtException);
425+
426+
@try
427+
{
428+
caughtException = NO;
429+
expectedLine = __LINE__; OCMReject([realObject addObject:@"foo"]);
430+
}
431+
@catch (NSException *e)
432+
{
433+
XCTAssertTrue([[e reason] containsString:expectedFile], @"`%@` should contain `%@`", [e reason], expectedFile);
434+
XCTAssertTrue([[e reason] containsString:[[NSNumber numberWithInt:expectedLine] stringValue]], @"`%@` should contain `%d`", [e reason], expectedLine);
435+
caughtException = YES;
436+
}
437+
XCTAssertTrue(caughtException);
438+
439+
}
440+
393441

394442
- (void)testCanExplicitlySelectClassMethodForStubs
395443
{

0 commit comments

Comments
 (0)