Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add location recording to all macros. #431

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions Source/OCMock/OCMMacroState.h
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
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 didRecordInvocation] == 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 didRecordInvocation] == 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"
Copy link

@tzvist tzvist May 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer that we would assert that the received object isKindOfClass OCMockObject in beginStubMacro/beginExpectMacro/beginRejectMacro(and this way give a much more informative message and have a proper stack trace)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry tzvist, could you clarify which received object are you talking about? If it's what I think you are saying, we don't have that info in the begin*Macro method.

Copy link

@tzvist tzvist Jun 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After looking at it some more I have misunderstand the way OCMock finds the class that is stubbed, apparently my proposal would not work.

@"- The selector conflicts with a selector implemented by OCMStubRecorder/OCMExpectationRecorder.";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give me an outline of how that would work?

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
5 changes: 5 additions & 0 deletions Source/OCMock/OCMRecorder.h
Expand Up @@ -16,6 +16,8 @@

#import <Foundation/Foundation.h>

#import "OCMLocation.h"

@class OCMockObject;
@class OCMInvocationMatcher;

Expand All @@ -28,6 +30,9 @@
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
Expand Up @@ -51,6 +51,7 @@ - (void)setShouldReturnMockFromInit:(BOOL)flag

- (void)dealloc
{
[_ocm_location release];
[invocationMatcher release];
[super dealloc];
}
Expand Down
4 changes: 1 addition & 3 deletions Source/OCMock/OCMVerifier.h
Expand Up @@ -16,13 +16,11 @@

#import <OCMock/OCMRecorder.h>

@class OCMLocation;
@class OCMQuantifier;

@interface OCMVerifier : OCMRecorder

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

- (id)withQuantifier:(OCMQuantifier *)quantifier;

Expand Down
3 changes: 1 addition & 2 deletions Source/OCMock/OCMVerifier.m
Expand Up @@ -44,12 +44,11 @@ - (id)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/OCMockMacros.h
Expand Up @@ -37,7 +37,7 @@
#define OCMStub(invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginStubMacro]; \
[OCMMacroState beginStubMacroAtLocation:OCMMakeLocation(nil, __FILE__, __LINE__)]; \
OCMStubRecorder *recorder = nil; \
@try{ \
invocation; \
Expand All @@ -55,7 +55,7 @@
#define OCMExpect(invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginExpectMacro]; \
[OCMMacroState beginExpectMacroAtLocation:OCMMakeLocation(nil, __FILE__, __LINE__)]; \
OCMStubRecorder *recorder = nil; \
@try{ \
invocation; \
Expand All @@ -73,7 +73,7 @@
#define OCMReject(invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginRejectMacro]; \
[OCMMacroState beginRejectMacroAtLocation:OCMMakeLocation(nil, __FILE__, __LINE__)]; \
OCMStubRecorder *recorder = nil; \
@try{ \
invocation; \
Expand Down
48 changes: 48 additions & 0 deletions Source/OCMockTests/OCMockObjectMacroTests.m
Expand Up @@ -454,6 +454,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