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

Allow OCMock to be used with EarlGrey for UITesting #481

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 26 additions & 8 deletions Source/OCMock/OCMStubRecorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#import <OCMock/OCMRecorder.h>

#import <objc/runtime.h>
#import <string.h>

#if !TARGET_OS_WATCH
@class XCTestExpectation;
Expand All @@ -42,15 +43,32 @@

@interface OCMStubRecorder (Properties)

#define andReturn(aValue) _andReturn(({ \
__typeof__(aValue) _val = (aValue); \
NSValue *_nsval = [NSValue value:&_val withObjCType:@encode(__typeof__(_val))]; \
if (OCMIsObjectType(@encode(__typeof(_val)))) { \
objc_setAssociatedObject(_nsval, "OCMAssociatedBoxedValue", *(__unsafe_unretained id *) (void *) &_val, OBJC_ASSOCIATION_RETAIN); \
} \
_nsval; \
// Used to autoselect `return` vs `returnValue` based on type.
// Gets complicated because NSValue cannot be NSCoded if it contains a type
// of void*, and typeof(nil) is void* in ObjC (but not ObjC++) code.
// Also we want to avoid undefined behaviours by casting _val into an id
// if it isn't a pointer type.
#define andReturn(aValue) _andReturn(({ \
__typeof__(aValue) _val = (aValue); \
const char *_encoding = @encode(__typeof(aValue)); \
const void *_nilPtr = nil; \
BOOL _objectOrNil = OCMIsObjectType(_encoding) || \
(strcmp(_encoding, @encode(void *)) == 0 && \
memcmp((void*)&_val, &_nilPtr, sizeof(void*)) == 0); \
id _retVal; \
if(_objectOrNil) \
{ \
__unsafe_unretained id _unsafeId; \
memcpy(&_unsafeId, (void*)&_val, sizeof(id)); \
_retVal = _unsafeId; \
} \
else \
{ \
_retVal = [NSValue valueWithBytes:&_val objCType:_encoding]; \
} \
[NSArray arrayWithObjects:@(_objectOrNil), _retVal, nil]; \
}))
@property (nonatomic, readonly) OCMStubRecorder *(^ _andReturn)(NSValue *);
@property (nonatomic, readonly) OCMStubRecorder *(^ _andReturn)(NSArray<id> *);

#define andThrow(anException) _andThrow(anException)
@property (nonatomic, readonly) OCMStubRecorder *(^ _andThrow)(NSException *);
Expand Down
21 changes: 7 additions & 14 deletions Source/OCMock/OCMStubRecorder.m
Original file line number Diff line number Diff line change
Expand Up @@ -126,21 +126,14 @@ @implementation OCMStubRecorder (Properties)

@dynamic _andReturn;

- (OCMStubRecorder * (^)(NSValue *))_andReturn
{
id (^theBlock)(id) = ^(NSValue *aValue) {
if(OCMIsObjectType([aValue objCType]))
{
id objValue = nil;
[aValue getValue:&objValue]; // TODO: deprecated but replacement available in 10.13 only
return [self andReturn:objValue];
}
else
{
return [self andReturnValue:aValue];
}
- (OCMStubRecorder *(^)(NSArray<id> *))_andReturn
{
id (^theBlock)(NSArray<id> *) = ^ (NSArray<id> *aTuple)
{
id value = (aTuple.count == 1) ? nil : aTuple[1];
return [aTuple[0] boolValue] ? [self andReturn:value] : [self andReturnValue:value];
};
return (id)[[theBlock copy] autorelease];
return [[theBlock copy] autorelease];
}


Expand Down
57 changes: 35 additions & 22 deletions Source/OCMock/OCMockMacros.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,68 +34,76 @@
#define OCMObserverMock() [OCMockObject observerMock]


#define OCMStub(invocation) \
#define OCMStubWithStateClass(MacroStateClass, invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginStubMacro]; \
[MacroStateClass beginStubMacro]; \
OCMStubRecorder *recorder = nil; \
@try{ \
invocation; \
}@catch(...){ \
[[OCMMacroState globalState] setInvocationDidThrow:YES]; \
[[MacroStateClass globalState] setInvocationDidThrow:YES]; \
/* NOLINTNEXTLINE(google-objc-avoid-throwing-exception) */ \
@throw; \
}@finally{ \
recorder = [OCMMacroState endStubMacro]; \
recorder = [MacroStateClass endStubMacro]; \
} \
recorder; \
); \
})

#define OCMExpect(invocation) \
#define OCMStub(invocation) OCMStubWithStateClass(OCMMacroState, invocation)

#define OCMExpectWithStateClass(MacroStateClass, invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginExpectMacro]; \
[MacroStateClass beginExpectMacro]; \
OCMStubRecorder *recorder = nil; \
@try{ \
invocation; \
}@catch(...){ \
[[OCMMacroState globalState] setInvocationDidThrow:YES]; \
[[MacroStateClass globalState] setInvocationDidThrow:YES]; \
/* NOLINTNEXTLINE(google-objc-avoid-throwing-exception) */ \
@throw; \
}@finally{ \
recorder = [OCMMacroState endExpectMacro]; \
recorder = [MacroStateClass endExpectMacro]; \
} \
recorder; \
); \
})

#define OCMReject(invocation) \
#define OCMExpect(invocation) OCMExpectWithStateClass(OCMMacroState, invocation)

#define OCMRejectWithStateClass(MacroStateClass, invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginRejectMacro]; \
[MacroStateClass beginRejectMacro]; \
OCMStubRecorder *recorder = nil; \
@try{ \
invocation; \
}@catch(...){ \
[[OCMMacroState globalState] setInvocationDidThrow:YES]; \
[[MacroStateClass globalState] setInvocationDidThrow:YES]; \
/* NOLINTNEXTLINE(google-objc-avoid-throwing-exception) */ \
@throw; \
}@finally{ \
recorder = [OCMMacroState endRejectMacro]; \
recorder = [MacroStateClass endRejectMacro]; \
} \
recorder; \
); \
})

#define OCMReject(invocation) OCMRejectWithStateClass(OCMMacroState, invocation)


#define OCMClassMethod(invocation) \

#define OCMClassMethodWithStateClass(MacroStateClass, invocation) \
_OCMSilenceWarnings( \
[[OCMMacroState globalState] switchToClassMethod]; \
[[MacroStateClass globalState] switchToClassMethod]; \
invocation; \
);

#define OCMClassMethod(invocation) OCMClassMethodWithStateClass(OCMMacroState, invocation)


#ifndef OCM_DISABLE_SHORT_SYNTAX
#define ClassMethod(invocation) OCMClassMethod(invocation)
Expand All @@ -106,38 +114,43 @@

#define OCMVerifyAllWithDelay(mock, delay) [(OCMockObject *)mock verifyWithDelay:delay atLocation:OCMMakeLocation(self, __FILE__, __LINE__)]

#define _OCMVerify(invocation) \
#define _OCMVerifyWithStateClass(MacroStateClass, invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginVerifyMacroAtLocation:OCMMakeLocation(self, __FILE__, __LINE__)]; \
[MacroStateClass beginVerifyMacroAtLocation:OCMMakeLocation(self, __FILE__, __LINE__)]; \
@try{ \
invocation; \
}@catch(...){ \
[[OCMMacroState globalState] setInvocationDidThrow:YES]; \
[[MacroStateClass globalState] setInvocationDidThrow:YES]; \
/* NOLINTNEXTLINE(google-objc-avoid-throwing-exception) */ \
@throw; \
}@finally{ \
[OCMMacroState endVerifyMacro]; \
[MacroStateClass endVerifyMacro]; \
} \
); \
})

#define _OCMVerifyWithQuantifier(quantifier, invocation) \
#define _OCMVerify(invocation) _OCMVerifyWithStateClass(OCMMacroState, invocation)


#define _OCMVerifyWithQuantifierAndStateClass(MacroStateClass, quantifier, invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginVerifyMacroAtLocation:OCMMakeLocation(self, __FILE__, __LINE__) withQuantifier:quantifier]; \
[MacroStateClass beginVerifyMacroAtLocation:OCMMakeLocation(self, __FILE__, __LINE__) withQuantifier:quantifier]; \
@try{ \
invocation; \
}@catch(...){ \
[[OCMMacroState globalState] setInvocationDidThrow:YES]; \
[[MacroStateClass globalState] setInvocationDidThrow:YES]; \
/* NOLINTNEXTLINE(google-objc-avoid-throwing-exception) */ \
@throw; \
}@finally{ \
[OCMMacroState endVerifyMacro]; \
[MacroStateClass endVerifyMacro]; \
} \
); \
})

#define _OCMVerifyWithQuantifier(quantifier, invocation) _OCMVerifyWithQuantifierAndStateClass(OCMMacroState, quantifier, invocation)

// explanation for macros below here: https://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros

#define _OCMVerify_1(A) _OCMVerify(A)
Expand Down