Skip to content

Commit 23c83e7

Browse files
committed
Handle using nil as argument for andReturn in ObjC++ code.
Fix for #403. This improves the special case handling of passing nil in return values to handle C++ cases that previously weren't working. Adds tests for C++11 and C++98 variants. To test fully tests need to be executed in both 32bit and 64 bit modes.
1 parent 90693d3 commit 23c83e7

6 files changed

+171
-22
lines changed

Source/OCMock.xcodeproj/project.pbxproj

+13-1
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,10 @@
279279
817EB15C1BD765130047E85A /* OCMBlockArgCaller.h in Headers */ = {isa = PBXBuildFile; fileRef = 2FA2891034E7B73AA3511D17 /* OCMBlockArgCaller.h */; };
280280
817EB15D1BD765130047E85A /* OCMArgAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 2FA2833B48908EAD36444671 /* OCMArgAction.h */; };
281281
817EB1661BD7674D0047E85A /* OCMFunctionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 03F370CA1BAA1DE800CAD3E8 /* OCMFunctionsPrivate.h */; };
282+
8B11D4B72448E2E900247BE2 /* OCMCPlusPlus98Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B11D4B62448E2E900247BE2 /* OCMCPlusPlus98Tests.mm */; settings = {COMPILER_FLAGS = "-std=gnu++98"; }; };
283+
8B11D4B82448E2F400247BE2 /* OCMCPlusPlus98Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B11D4B62448E2E900247BE2 /* OCMCPlusPlus98Tests.mm */; settings = {COMPILER_FLAGS = "-std=gnu++98"; }; };
284+
8B11D4BA2448E53600247BE2 /* OCMCPlusPlus11Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B11D4B92448E53600247BE2 /* OCMCPlusPlus11Tests.mm */; settings = {COMPILER_FLAGS = "-std=gnu++11"; }; };
285+
8B11D4BB2448E53600247BE2 /* OCMCPlusPlus11Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B11D4B92448E53600247BE2 /* OCMCPlusPlus11Tests.mm */; settings = {COMPILER_FLAGS = "-std=gnu++11"; }; };
282286
8DE97C5522B43EE60098C63F /* OCMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 03B3159E146333BF0052CD09 /* OCMockObject.m */; };
283287
8DE97C5622B43EE60098C63F /* OCClassMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 03B3158C146333BF0052CD09 /* OCClassMockObject.m */; };
284288
8DE97C5722B43EE60098C63F /* OCPartialMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 03B315AA146333BF0052CD09 /* OCPartialMockObject.m */; };
@@ -545,7 +549,7 @@
545549
2FA28006D043CBDBBAEF6E3F /* OCMMacroState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMMacroState.h; sourceTree = "<group>"; };
546550
2FA280987F4EA8A4D79000D0 /* OCMMacroState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMMacroState.m; sourceTree = "<group>"; };
547551
2FA280EB5E8CDEEAE76861F7 /* OCMNonRetainingObjectReturnValueProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMNonRetainingObjectReturnValueProvider.m; sourceTree = "<group>"; };
548-
2FA2813F93050582D83E1499 /* OCMockObjectRuntimeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMockObjectRuntimeTests.m; sourceTree = "<group>"; };
552+
2FA2813F93050582D83E1499 /* OCMockObjectRuntimeTests.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = OCMockObjectRuntimeTests.m; sourceTree = "<group>"; };
549553
2FA2822E19948FC997965267 /* OCMockObjectTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMockObjectTests.m; sourceTree = "<group>"; };
550554
2FA2833B48908EAD36444671 /* OCMArgAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMArgAction.h; sourceTree = "<group>"; };
551555
2FA283D58AA7569D8A5B0C57 /* OCMBlockArgCaller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMBlockArgCaller.m; sourceTree = "<group>"; };
@@ -569,6 +573,8 @@
569573
3CFBDD751BB3DB200050D9C5 /* TestClassWithCustomReferenceCounting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestClassWithCustomReferenceCounting.h; sourceTree = "<group>"; };
570574
3CFBDD761BB3DB200050D9C5 /* TestClassWithCustomReferenceCounting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestClassWithCustomReferenceCounting.m; sourceTree = "<group>"; };
571575
817EB1621BD765130047E85A /* OCMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OCMock.framework; sourceTree = BUILT_PRODUCTS_DIR; };
576+
8B11D4B62448E2E900247BE2 /* OCMCPlusPlus98Tests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OCMCPlusPlus98Tests.mm; sourceTree = "<group>"; };
577+
8B11D4B92448E53600247BE2 /* OCMCPlusPlus11Tests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OCMCPlusPlus11Tests.mm; sourceTree = "<group>"; };
572578
8DE97CA022B43EE60098C63F /* OCMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OCMock.framework; sourceTree = BUILT_PRODUCTS_DIR; };
573579
A02926811CA0725A00594AAF /* TestObjects.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TestObjects.xcdatamodel; sourceTree = "<group>"; };
574580
D31108AD1828DB8700737925 /* OCMockLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OCMockLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -747,6 +753,8 @@
747753
037ECD5318FAD84100AF0E4C /* OCMInvocationMatcherTests.m */,
748754
031E50571BB4A56300E257C3 /* OCMBoxedReturnValueProviderTests.m */,
749755
03B316211463350E0052CD09 /* OCMConstraintTests.m */,
756+
8B11D4B62448E2E900247BE2 /* OCMCPlusPlus98Tests.mm */,
757+
8B11D4B92448E53600247BE2 /* OCMCPlusPlus11Tests.mm */,
750758
2FA28EDBF243639C57F88A1B /* OCMArgTests.m */,
751759
036865631D3571A8005E6BEE /* OCMQuantifierTests.m */,
752760
03B316291463350E0052CD09 /* OCObserverMockObjectTests.m */,
@@ -1499,8 +1507,10 @@
14991507
03565A4818F05721003AE91E /* OCMStubRecorderTests.m in Sources */,
15001508
03565A4518F05721003AE91E /* OCMockObjectForwardingTargetTests.m in Sources */,
15011509
2FA28FA53C57236B6DD64E82 /* OCMockObjectRuntimeTests.m in Sources */,
1510+
8B11D4BA2448E53600247BE2 /* OCMCPlusPlus11Tests.mm in Sources */,
15021511
2FA2839F33289795284C32FB /* OCMockObjectTests.m in Sources */,
15031512
038599F723807B06002B3ABE /* OCMockObjectInternalTests.m in Sources */,
1513+
8B11D4B72448E2E900247BE2 /* OCMCPlusPlus98Tests.mm in Sources */,
15041514
2FA28AB33F01A7D980F2C705 /* OCMockObjectDynamicPropertyMockingTests.m in Sources */,
15051515
031E50581BB4A56300E257C3 /* OCMBoxedReturnValueProviderTests.m in Sources */,
15061516
);
@@ -1611,8 +1621,10 @@
16111621
03C9CA1D18F05A75006DF94D /* OCMockObjectProtocolMocksTests.m in Sources */,
16121622
03E98D5118F310EE00522D42 /* OCMockObjectMacroTests.m in Sources */,
16131623
A06930951CA1BFC900513023 /* TestObjects.xcdatamodeld in Sources */,
1624+
8B11D4BB2448E53600247BE2 /* OCMCPlusPlus11Tests.mm in Sources */,
16141625
2FA28295E1F58F40A77D7448 /* OCMockObjectRuntimeTests.m in Sources */,
16151626
038599F823807B06002B3ABE /* OCMockObjectInternalTests.m in Sources */,
1627+
8B11D4B82448E2F400247BE2 /* OCMCPlusPlus98Tests.mm in Sources */,
16161628
2FA28246CD449A01717B1CEC /* OCMockObjectTests.m in Sources */,
16171629
2FA28F12AAD384A8CB16094B /* OCMockObjectDynamicPropertyMockingTests.m in Sources */,
16181630
);

Source/OCMock/OCMBoxedReturnValueProvider.m

+43-7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@
1818
#import "OCMFunctionsPrivate.h"
1919
#import "NSValue+OCMAdditions.h"
2020

21+
static BOOL IsZeroBuffer(const char* buffer, size_t length)
22+
{
23+
for (size_t i = 0; i < length; ++i)
24+
{
25+
if (buffer[i] != 0)
26+
{
27+
return NO;
28+
}
29+
}
30+
return YES;
31+
}
32+
2133
@implementation OCMBoxedReturnValueProvider
2234

2335
- (void)handleInvocation:(NSInvocation *)anInvocation
@@ -26,10 +38,13 @@ - (void)handleInvocation:(NSInvocation *)anInvocation
2638
NSUInteger returnTypeSize = [[anInvocation methodSignature] methodReturnLength];
2739
char valueBuffer[returnTypeSize];
2840
NSValue *returnValueAsNSValue = (NSValue *)returnValue;
41+
[returnValueAsNSValue getValue:valueBuffer];
2942

30-
if([self isMethodReturnType:returnType compatibleWithValueType:[returnValueAsNSValue objCType]])
43+
if([self isMethodReturnType:returnType
44+
compatibleWithValueType:[returnValueAsNSValue objCType]
45+
value:valueBuffer
46+
valueSize:returnTypeSize])
3147
{
32-
[returnValueAsNSValue getValue:valueBuffer];
3348
[anInvocation setReturnValue:valueBuffer];
3449
}
3550
else if([returnValueAsNSValue getBytes:valueBuffer objCType:returnType])
@@ -43,16 +58,37 @@ - (void)handleInvocation:(NSInvocation *)anInvocation
4358
}
4459
}
4560

46-
47-
- (BOOL)isMethodReturnType:(const char *)returnType compatibleWithValueType:(const char *)valueType
61+
- (BOOL)isMethodReturnType:(const char *)returnType compatibleWithValueType:(const char *)valueType value:(const char*)value valueSize:(size_t)valueSize
4862
{
4963
/* Same types are obviously compatible */
5064
if(strcmp(returnType, valueType) == 0)
5165
return YES;
5266

53-
/* Allow void* for methods that return id, mainly to be able to handle nil */
54-
if(strcmp(returnType, @encode(id)) == 0 && strcmp(valueType, @encode(void *)) == 0)
55-
return YES;
67+
// Special casing for nil.
68+
if(strcmp(returnType, @encode(id)) == 0)
69+
{
70+
// Check to verify that the value is actually zero.
71+
if(IsZeroBuffer(value, valueSize))
72+
{
73+
// nil and Nil get potentially different encodings depending on the compilation
74+
// settings of the file where the return value gets recorded. We check to verify
75+
// against all the values we know of.
76+
const char *validNilEncodings[] =
77+
{
78+
@encode(void *), // Standard Obj C
79+
@encode(int), // 32 bit C++ (before nullptr)
80+
@encode(long long), // 64 bit C++ (before nullptr)
81+
@encode(char *), // C++ with nullptr
82+
};
83+
for (size_t i = 0; i < sizeof(validNilEncodings) / sizeof(validNilEncodings[0]); ++i)
84+
{
85+
if (strcmp(valueType, validNilEncodings[i]) == 0)
86+
{
87+
return YES;
88+
}
89+
}
90+
}
91+
}
5692

5793
return OCMEqualTypesAllowingOpaqueStructs(returnType, valueType);
5894
}

Source/OCMockTests/OCMBoxedReturnValueProviderTests.m

+20-11
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,24 @@
1818
#import "OCMBoxedReturnValueProvider.h"
1919

2020
@interface OCMBoxedReturnValueProvider(Private)
21-
- (BOOL)isMethodReturnType:(const char *)returnType compatibleWithValueType:(const char *)valueType;
21+
- (BOOL)isMethodReturnType:(const char *)returnType compatibleWithValueType:(const char *)valueType value:(const char*)value valueSize:(size_t)valueSize;
2222
@end
2323

2424
@interface OCMBoxedReturnValueProviderTests : XCTestCase
25-
25+
{
26+
char value;
27+
size_t valueSize;
28+
}
2629
@end
2730

2831
@implementation OCMBoxedReturnValueProviderTests
2932

33+
- (void)setUp {
34+
[super setUp];
35+
value = 'A';
36+
valueSize = 1;
37+
}
38+
3039
- (void)testCorrectEqualityForCppProperty
3140
{
3241
// see https://github.com/erikdoe/ocmock/issues/96
@@ -53,12 +62,12 @@ - (void)testCorrectEqualityForCppProperty
5362
"r^{GURL}";
5463

5564
OCMBoxedReturnValueProvider *boxed = [OCMBoxedReturnValueProvider new];
56-
XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2]);
57-
XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type3]);
58-
XCTAssertTrue([boxed isMethodReturnType:type2 compatibleWithValueType:type1]);
59-
XCTAssertTrue([boxed isMethodReturnType:type2 compatibleWithValueType:type3]);
60-
XCTAssertTrue([boxed isMethodReturnType:type3 compatibleWithValueType:type1]);
61-
XCTAssertTrue([boxed isMethodReturnType:type3 compatibleWithValueType:type2]);
65+
XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:&value valueSize:valueSize]);
66+
XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type3 value:&value valueSize:valueSize]);
67+
XCTAssertTrue([boxed isMethodReturnType:type2 compatibleWithValueType:type1 value:&value valueSize:valueSize]);
68+
XCTAssertTrue([boxed isMethodReturnType:type2 compatibleWithValueType:type3 value:&value valueSize:valueSize]);
69+
XCTAssertTrue([boxed isMethodReturnType:type3 compatibleWithValueType:type1 value:&value valueSize:valueSize]);
70+
XCTAssertTrue([boxed isMethodReturnType:type3 compatibleWithValueType:type2 value:&value valueSize:valueSize]);
6271
}
6372

6473

@@ -78,7 +87,7 @@ - (void)testCorrectEqualityForCppReturnTypesWithVtables
7887
"ar> >={__rep=(?={__long=QQ*}{__short=(?=Cc)[23c]}{__raw=[3Q]})}}}}";
7988

8089
OCMBoxedReturnValueProvider *boxed = [OCMBoxedReturnValueProvider new];
81-
XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2]);
90+
XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:&value valueSize:valueSize]);
8291
}
8392

8493

@@ -89,7 +98,7 @@ - (void)testCorrectEqualityForStructureWithUnknownName
8998
const char *type2 = "{CLLocationCoordinate2D=dd}";
9099

91100
OCMBoxedReturnValueProvider *boxed = [OCMBoxedReturnValueProvider new];
92-
XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2]);
101+
XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:&value valueSize:valueSize]);
93102

94103
}
95104

@@ -115,7 +124,7 @@ - (void)testCorrectEqualityForStructureWithoutName
115124
"pressed_pair<GURL *, std::__1::default_delete<GURL> >=^{GURL}}}}";
116125

117126
OCMBoxedReturnValueProvider *boxed = [OCMBoxedReturnValueProvider new];
118-
XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2]);
127+
XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:&value valueSize:valueSize]);
119128

120129
}
121130

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2020 Erik Doernenburg and contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
* not use these files except in compliance with the License. You may obtain
6+
* a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
#import <XCTest/XCTest.h>
18+
#import <OCMock/OCMock.h>
19+
20+
#if !defined(__cplusplus)
21+
#error This file must be compiled with C++
22+
#endif
23+
24+
#if !__has_feature(cxx_nullptr)
25+
#error This file must be compiled with a version of C++ that supports nullptr
26+
#endif
27+
28+
@interface OCMCPlusPlus11Tests : XCTestCase
29+
@end
30+
31+
32+
@implementation OCMCPlusPlus11Tests
33+
34+
- (void)testSetsUpStubReturningNilForIdReturnType
35+
{
36+
id mock = OCMPartialMock([NSArray arrayWithObject:@"Foo"]);
37+
38+
OCMExpect([mock lastObject]).andReturn(nil);
39+
XCTAssertNil([mock lastObject], @"Should have returned stubbed value");
40+
41+
OCMExpect([mock lastObject]).andReturn(Nil);
42+
XCTAssertNil([mock lastObject], @"Should have returned stubbed value");
43+
}
44+
45+
@end
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2020 Erik Doernenburg and contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
* not use these files except in compliance with the License. You may obtain
6+
* a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
#import <XCTest/XCTest.h>
18+
#import <OCMock/OCMock.h>
19+
20+
#if !defined(__cplusplus)
21+
#error This file must be compiled with C++
22+
#endif
23+
24+
#if __has_feature(cxx_nullptr)
25+
#error This file must be compiled with a version of C++ (98) that doesn't support nullptr
26+
#endif
27+
28+
@interface OCMCPlusPlus98Tests : XCTestCase
29+
@end
30+
31+
32+
@implementation OCMCPlusPlus98Tests
33+
34+
- (void)testSetsUpStubReturningNilForIdReturnType
35+
{
36+
id mock = OCMPartialMock([NSArray arrayWithObject:@"Foo"]);
37+
38+
OCMExpect([mock lastObject]).andReturn(nil);
39+
XCTAssertNil([mock lastObject], @"Should have returned stubbed value");
40+
41+
OCMExpect([mock lastObject]).andReturn(Nil);
42+
XCTAssertNil([mock lastObject], @"Should have returned stubbed value");
43+
}
44+
45+
@end

Source/OCMockTests/OCMockObjectMacroTests.m

+5-3
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,13 @@ - (void)testSetsUpStubsWithStructureReturnValues
177177

178178
- (void)testSetsUpStubReturningNilForIdReturnType
179179
{
180-
id mock = OCMClassMock([NSString class]);
180+
id mock = OCMPartialMock([NSArray arrayWithObject:@"Foo"]);
181181

182-
OCMStub([mock lowercaseString]).andReturn(nil);
182+
OCMExpect([mock lastObject]).andReturn(nil);
183+
XCTAssertNil([mock lastObject], @"Should have returned stubbed value");
183184

184-
XCTAssertNil([mock lowercaseString], @"Should have returned stubbed value");
185+
OCMExpect([mock lastObject]).andReturn(Nil);
186+
XCTAssertNil([mock lastObject], @"Should have returned stubbed value");
185187
}
186188

187189
- (void)testSetsUpExceptionThrowing

0 commit comments

Comments
 (0)