From 4fd7245d966984caa3c1b31fe30110e8bc0b8ff0 Mon Sep 17 00:00:00 2001 From: Dave MacLachlan Date: Thu, 16 Apr 2020 14:01:45 -0700 Subject: [PATCH 1/3] 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. --- Source/OCMock.xcodeproj/project.pbxproj | 14 +++++- Source/OCMock/OCMBoxedReturnValueProvider.m | 50 ++++++++++++++++--- .../OCMBoxedReturnValueProviderTests.m | 31 ++++++++---- Source/OCMockTests/OCMCPlusPlus11Tests.mm | 45 +++++++++++++++++ Source/OCMockTests/OCMCPlusPlus98Tests.mm | 45 +++++++++++++++++ Source/OCMockTests/OCMockObjectMacroTests.m | 35 +++++++++++-- 6 files changed, 198 insertions(+), 22 deletions(-) create mode 100644 Source/OCMockTests/OCMCPlusPlus11Tests.mm create mode 100644 Source/OCMockTests/OCMCPlusPlus98Tests.mm diff --git a/Source/OCMock.xcodeproj/project.pbxproj b/Source/OCMock.xcodeproj/project.pbxproj index b72a587b..5b9fd8bc 100644 --- a/Source/OCMock.xcodeproj/project.pbxproj +++ b/Source/OCMock.xcodeproj/project.pbxproj @@ -281,6 +281,10 @@ 817EB1661BD7674D0047E85A /* OCMFunctionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 03F370CA1BAA1DE800CAD3E8 /* OCMFunctionsPrivate.h */; }; 8BF73E53246CA75E00B9A52C /* OCMNoEscapeBlockTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BF73E52246CA75E00B9A52C /* OCMNoEscapeBlockTests.m */; settings = {COMPILER_FLAGS = "-Xclang -fexperimental-optimized-noescape"; }; }; 8BF73E54246CA75E00B9A52C /* OCMNoEscapeBlockTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BF73E52246CA75E00B9A52C /* OCMNoEscapeBlockTests.m */; settings = {COMPILER_FLAGS = "-Xclang -fexperimental-optimized-noescape"; }; }; + 8B11D4B72448E2E900247BE2 /* OCMCPlusPlus98Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B11D4B62448E2E900247BE2 /* OCMCPlusPlus98Tests.mm */; settings = {COMPILER_FLAGS = "-std=gnu++98"; }; }; + 8B11D4B82448E2F400247BE2 /* OCMCPlusPlus98Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B11D4B62448E2E900247BE2 /* OCMCPlusPlus98Tests.mm */; settings = {COMPILER_FLAGS = "-std=gnu++98"; }; }; + 8B11D4BA2448E53600247BE2 /* OCMCPlusPlus11Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B11D4B92448E53600247BE2 /* OCMCPlusPlus11Tests.mm */; settings = {COMPILER_FLAGS = "-std=gnu++11"; }; }; + 8B11D4BB2448E53600247BE2 /* OCMCPlusPlus11Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B11D4B92448E53600247BE2 /* OCMCPlusPlus11Tests.mm */; settings = {COMPILER_FLAGS = "-std=gnu++11"; }; }; 8DE97C5522B43EE60098C63F /* OCMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 03B3159E146333BF0052CD09 /* OCMockObject.m */; }; 8DE97C5622B43EE60098C63F /* OCClassMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 03B3158C146333BF0052CD09 /* OCClassMockObject.m */; }; 8DE97C5722B43EE60098C63F /* OCPartialMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 03B315AA146333BF0052CD09 /* OCPartialMockObject.m */; }; @@ -547,7 +551,7 @@ 2FA28006D043CBDBBAEF6E3F /* OCMMacroState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMMacroState.h; sourceTree = ""; }; 2FA280987F4EA8A4D79000D0 /* OCMMacroState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMMacroState.m; sourceTree = ""; }; 2FA280EB5E8CDEEAE76861F7 /* OCMNonRetainingObjectReturnValueProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMNonRetainingObjectReturnValueProvider.m; sourceTree = ""; }; - 2FA2813F93050582D83E1499 /* OCMockObjectRuntimeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMockObjectRuntimeTests.m; sourceTree = ""; }; + 2FA2813F93050582D83E1499 /* OCMockObjectRuntimeTests.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = OCMockObjectRuntimeTests.m; sourceTree = ""; }; 2FA2822E19948FC997965267 /* OCMockObjectTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMockObjectTests.m; sourceTree = ""; }; 2FA2833B48908EAD36444671 /* OCMArgAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMArgAction.h; sourceTree = ""; }; 2FA283D58AA7569D8A5B0C57 /* OCMBlockArgCaller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMBlockArgCaller.m; sourceTree = ""; }; @@ -572,6 +576,8 @@ 3CFBDD761BB3DB200050D9C5 /* TestClassWithCustomReferenceCounting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestClassWithCustomReferenceCounting.m; sourceTree = ""; }; 817EB1621BD765130047E85A /* OCMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OCMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8BF73E52246CA75E00B9A52C /* OCMNoEscapeBlockTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMNoEscapeBlockTests.m; sourceTree = ""; }; + 8B11D4B62448E2E900247BE2 /* OCMCPlusPlus98Tests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OCMCPlusPlus98Tests.mm; sourceTree = ""; }; + 8B11D4B92448E53600247BE2 /* OCMCPlusPlus11Tests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OCMCPlusPlus11Tests.mm; sourceTree = ""; }; 8DE97CA022B43EE60098C63F /* OCMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OCMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A02926811CA0725A00594AAF /* TestObjects.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TestObjects.xcdatamodel; sourceTree = ""; }; D31108AD1828DB8700737925 /* OCMockLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OCMockLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -751,6 +757,8 @@ 037ECD5318FAD84100AF0E4C /* OCMInvocationMatcherTests.m */, 031E50571BB4A56300E257C3 /* OCMBoxedReturnValueProviderTests.m */, 03B316211463350E0052CD09 /* OCMConstraintTests.m */, + 8B11D4B62448E2E900247BE2 /* OCMCPlusPlus98Tests.mm */, + 8B11D4B92448E53600247BE2 /* OCMCPlusPlus11Tests.mm */, 2FA28EDBF243639C57F88A1B /* OCMArgTests.m */, 036865631D3571A8005E6BEE /* OCMQuantifierTests.m */, 03B316291463350E0052CD09 /* OCObserverMockObjectTests.m */, @@ -1503,9 +1511,11 @@ 03565A4818F05721003AE91E /* OCMStubRecorderTests.m in Sources */, 03565A4518F05721003AE91E /* OCMockObjectForwardingTargetTests.m in Sources */, 2FA28FA53C57236B6DD64E82 /* OCMockObjectRuntimeTests.m in Sources */, + 8B11D4BA2448E53600247BE2 /* OCMCPlusPlus11Tests.mm in Sources */, 2FA2839F33289795284C32FB /* OCMockObjectTests.m in Sources */, 038599F723807B06002B3ABE /* OCMockObjectInternalTests.m in Sources */, 8BF73E53246CA75E00B9A52C /* OCMNoEscapeBlockTests.m in Sources */, + 8B11D4B72448E2E900247BE2 /* OCMCPlusPlus98Tests.mm in Sources */, 2FA28AB33F01A7D980F2C705 /* OCMockObjectDynamicPropertyMockingTests.m in Sources */, 031E50581BB4A56300E257C3 /* OCMBoxedReturnValueProviderTests.m in Sources */, ); @@ -1616,9 +1626,11 @@ 03C9CA1D18F05A75006DF94D /* OCMockObjectProtocolMocksTests.m in Sources */, 03E98D5118F310EE00522D42 /* OCMockObjectMacroTests.m in Sources */, A06930951CA1BFC900513023 /* TestObjects.xcdatamodeld in Sources */, + 8B11D4BB2448E53600247BE2 /* OCMCPlusPlus11Tests.mm in Sources */, 2FA28295E1F58F40A77D7448 /* OCMockObjectRuntimeTests.m in Sources */, 038599F823807B06002B3ABE /* OCMockObjectInternalTests.m in Sources */, 8BF73E54246CA75E00B9A52C /* OCMNoEscapeBlockTests.m in Sources */, + 8B11D4B82448E2F400247BE2 /* OCMCPlusPlus98Tests.mm in Sources */, 2FA28246CD449A01717B1CEC /* OCMockObjectTests.m in Sources */, 2FA28F12AAD384A8CB16094B /* OCMockObjectDynamicPropertyMockingTests.m in Sources */, ); diff --git a/Source/OCMock/OCMBoxedReturnValueProvider.m b/Source/OCMock/OCMBoxedReturnValueProvider.m index c29c8d2f..43edef02 100644 --- a/Source/OCMock/OCMBoxedReturnValueProvider.m +++ b/Source/OCMock/OCMBoxedReturnValueProvider.m @@ -18,6 +18,18 @@ #import "OCMFunctionsPrivate.h" #import "NSValue+OCMAdditions.h" +static BOOL IsZeroBuffer(const char* buffer, size_t length) +{ + for(size_t i = 0; i < length; ++i) + { + if(buffer[i] != 0) + { + return NO; + } + } + return YES; +} + @implementation OCMBoxedReturnValueProvider - (void)handleInvocation:(NSInvocation *)anInvocation @@ -26,10 +38,13 @@ - (void)handleInvocation:(NSInvocation *)anInvocation NSUInteger returnTypeSize = [[anInvocation methodSignature] methodReturnLength]; char valueBuffer[returnTypeSize]; NSValue *returnValueAsNSValue = (NSValue *)returnValue; + [returnValueAsNSValue getValue:valueBuffer]; - if([self isMethodReturnType:returnType compatibleWithValueType:[returnValueAsNSValue objCType]]) + if([self isMethodReturnType:returnType + compatibleWithValueType:[returnValueAsNSValue objCType] + value:valueBuffer + valueSize:returnTypeSize]) { - [returnValueAsNSValue getValue:valueBuffer]; [anInvocation setReturnValue:valueBuffer]; } else if([returnValueAsNSValue getBytes:valueBuffer objCType:returnType]) @@ -43,16 +58,37 @@ - (void)handleInvocation:(NSInvocation *)anInvocation } } - -- (BOOL)isMethodReturnType:(const char *)returnType compatibleWithValueType:(const char *)valueType +- (BOOL)isMethodReturnType:(const char *)returnType compatibleWithValueType:(const char *)valueType value:(const char*)value valueSize:(size_t)valueSize { /* Same types are obviously compatible */ if(strcmp(returnType, valueType) == 0) return YES; - /* Allow void* for methods that return id, mainly to be able to handle nil */ - if(strcmp(returnType, @encode(id)) == 0 && strcmp(valueType, @encode(void *)) == 0) - return YES; + // Special casing for nil and Nil + if(strcmp(returnType, @encode(id)) == 0 || strcmp(returnType, @encode(Class)) == 0) + { + // Check to verify that the value is actually zero. + if(IsZeroBuffer(value, valueSize)) + { + // nil and Nil get potentially different encodings depending on the compilation + // settings of the file where the return value gets recorded. We check to verify + // against all the values we know of. + const char *validNilEncodings[] = + { + @encode(void *), // Standard Obj C + @encode(int), // 32 bit C++ (before nullptr) + @encode(long long), // 64 bit C++ (before nullptr) + @encode(char *), // C++ with nullptr + }; + for(size_t i = 0; i < sizeof(validNilEncodings) / sizeof(validNilEncodings[0]); ++i) + { + if(strcmp(valueType, validNilEncodings[i]) == 0) + { + return YES; + } + } + } + } return OCMEqualTypesAllowingOpaqueStructs(returnType, valueType); } diff --git a/Source/OCMockTests/OCMBoxedReturnValueProviderTests.m b/Source/OCMockTests/OCMBoxedReturnValueProviderTests.m index a5f0c2be..a1273730 100644 --- a/Source/OCMockTests/OCMBoxedReturnValueProviderTests.m +++ b/Source/OCMockTests/OCMBoxedReturnValueProviderTests.m @@ -18,15 +18,24 @@ #import "OCMBoxedReturnValueProvider.h" @interface OCMBoxedReturnValueProvider(Private) -- (BOOL)isMethodReturnType:(const char *)returnType compatibleWithValueType:(const char *)valueType; +- (BOOL)isMethodReturnType:(const char *)returnType compatibleWithValueType:(const char *)valueType value:(const char*)value valueSize:(size_t)valueSize; @end @interface OCMBoxedReturnValueProviderTests : XCTestCase - +{ + char value; + size_t valueSize; +} @end @implementation OCMBoxedReturnValueProviderTests +- (void)setUp { + [super setUp]; + value = 'A'; + valueSize = 1; +} + - (void)testCorrectEqualityForCppProperty { // see https://github.com/erikdoe/ocmock/issues/96 @@ -53,12 +62,12 @@ - (void)testCorrectEqualityForCppProperty "r^{GURL}"; OCMBoxedReturnValueProvider *boxed = [OCMBoxedReturnValueProvider new]; - XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2]); - XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type3]); - XCTAssertTrue([boxed isMethodReturnType:type2 compatibleWithValueType:type1]); - XCTAssertTrue([boxed isMethodReturnType:type2 compatibleWithValueType:type3]); - XCTAssertTrue([boxed isMethodReturnType:type3 compatibleWithValueType:type1]); - XCTAssertTrue([boxed isMethodReturnType:type3 compatibleWithValueType:type2]); + XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:&value valueSize:valueSize]); + XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type3 value:&value valueSize:valueSize]); + XCTAssertTrue([boxed isMethodReturnType:type2 compatibleWithValueType:type1 value:&value valueSize:valueSize]); + XCTAssertTrue([boxed isMethodReturnType:type2 compatibleWithValueType:type3 value:&value valueSize:valueSize]); + XCTAssertTrue([boxed isMethodReturnType:type3 compatibleWithValueType:type1 value:&value valueSize:valueSize]); + XCTAssertTrue([boxed isMethodReturnType:type3 compatibleWithValueType:type2 value:&value valueSize:valueSize]); } @@ -78,7 +87,7 @@ - (void)testCorrectEqualityForCppReturnTypesWithVtables "ar> >={__rep=(?={__long=QQ*}{__short=(?=Cc)[23c]}{__raw=[3Q]})}}}}"; OCMBoxedReturnValueProvider *boxed = [OCMBoxedReturnValueProvider new]; - XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2]); + XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:&value valueSize:valueSize]); } @@ -89,7 +98,7 @@ - (void)testCorrectEqualityForStructureWithUnknownName const char *type2 = "{CLLocationCoordinate2D=dd}"; OCMBoxedReturnValueProvider *boxed = [OCMBoxedReturnValueProvider new]; - XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2]); + XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:&value valueSize:valueSize]); } @@ -115,7 +124,7 @@ - (void)testCorrectEqualityForStructureWithoutName "pressed_pair >=^{GURL}}}}"; OCMBoxedReturnValueProvider *boxed = [OCMBoxedReturnValueProvider new]; - XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2]); + XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:&value valueSize:valueSize]); } diff --git a/Source/OCMockTests/OCMCPlusPlus11Tests.mm b/Source/OCMockTests/OCMCPlusPlus11Tests.mm new file mode 100644 index 00000000..58c09349 --- /dev/null +++ b/Source/OCMockTests/OCMCPlusPlus11Tests.mm @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import +#import + +#if !defined(__cplusplus) +#error This file must be compiled with C++ +#endif + +#if !__has_feature(cxx_nullptr) +#error This file must be compiled with a version of C++ that supports nullptr +#endif + +@interface OCMCPlusPlus11Tests : XCTestCase +@end + + +@implementation OCMCPlusPlus11Tests + +- (void)testSetsUpStubReturningNilForIdReturnType +{ + id mock = OCMPartialMock([NSArray arrayWithObject:@"Foo"]); + + OCMExpect([mock lastObject]).andReturn(nil); + XCTAssertNil([mock lastObject], @"Should have returned stubbed value"); + + OCMExpect([mock lastObject]).andReturn(Nil); + XCTAssertNil([mock lastObject], @"Should have returned stubbed value"); +} + +@end diff --git a/Source/OCMockTests/OCMCPlusPlus98Tests.mm b/Source/OCMockTests/OCMCPlusPlus98Tests.mm new file mode 100644 index 00000000..891149b0 --- /dev/null +++ b/Source/OCMockTests/OCMCPlusPlus98Tests.mm @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import +#import + +#if !defined(__cplusplus) +#error This file must be compiled with C++ +#endif + +#if __has_feature(cxx_nullptr) +#error This file must be compiled with a version of C++ (98) that doesn't support nullptr +#endif + +@interface OCMCPlusPlus98Tests : XCTestCase +@end + + +@implementation OCMCPlusPlus98Tests + +- (void)testSetsUpStubReturningNilForIdReturnType +{ + id mock = OCMPartialMock([NSArray arrayWithObject:@"Foo"]); + + OCMExpect([mock lastObject]).andReturn(nil); + XCTAssertNil([mock lastObject], @"Should have returned stubbed value"); + + OCMExpect([mock lastObject]).andReturn(Nil); + XCTAssertNil([mock lastObject], @"Should have returned stubbed value"); +} + +@end diff --git a/Source/OCMockTests/OCMockObjectMacroTests.m b/Source/OCMockTests/OCMockObjectMacroTests.m index c4ba9f87..0fda8636 100644 --- a/Source/OCMockTests/OCMockObjectMacroTests.m +++ b/Source/OCMockTests/OCMockObjectMacroTests.m @@ -50,6 +50,22 @@ - (NSDecimalNumber*)method { @end +@interface TestClassWithClassReturnMethod : NSObject + +- (Class)method; + +@end + +@implementation TestClassWithClassReturnMethod + +- (Class)method +{ + return [self class]; +} + +@end + + // implemented in OCMockObjectClassMethodMockingTests @@ -177,11 +193,24 @@ - (void)testSetsUpStubsWithStructureReturnValues - (void)testSetsUpStubReturningNilForIdReturnType { - id mock = OCMClassMock([NSString class]); + id mock = OCMPartialMock([NSArray arrayWithObject:@"Foo"]); + + OCMExpect([mock lastObject]).andReturn(nil); + XCTAssertNil([mock lastObject], @"Should have returned stubbed value"); + + OCMExpect([mock lastObject]).andReturn(Nil); + XCTAssertNil([mock lastObject], @"Should have returned stubbed value"); +} + +- (void)testSetsUpStubReturningNilForClassReturnType +{ + id mock = OCMPartialMock([[TestClassWithClassReturnMethod alloc] init]); - OCMStub([mock lowercaseString]).andReturn(nil); + OCMExpect([mock method]).andReturn(nil); + XCTAssertNil([mock method], @"Should have returned stubbed value"); - XCTAssertNil([mock lowercaseString], @"Should have returned stubbed value"); + OCMExpect([mock method]).andReturn(Nil); + XCTAssertNil([mock method], @"Should have returned stubbed value"); } - (void)testSetsUpExceptionThrowing From 34dc3f849d21496ad6e8f29ebf2b4c766c7409e9 Mon Sep 17 00:00:00 2001 From: Erik Doernenburg Date: Tue, 14 Jul 2020 20:49:47 +0200 Subject: [PATCH 2/3] Fixed file type. --- Source/OCMock.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/OCMock.xcodeproj/project.pbxproj b/Source/OCMock.xcodeproj/project.pbxproj index 7687d725..dfaa8cbf 100644 --- a/Source/OCMock.xcodeproj/project.pbxproj +++ b/Source/OCMock.xcodeproj/project.pbxproj @@ -279,12 +279,12 @@ 817EB15C1BD765130047E85A /* OCMBlockArgCaller.h in Headers */ = {isa = PBXBuildFile; fileRef = 2FA2891034E7B73AA3511D17 /* OCMBlockArgCaller.h */; }; 817EB15D1BD765130047E85A /* OCMArgAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 2FA2833B48908EAD36444671 /* OCMArgAction.h */; }; 817EB1661BD7674D0047E85A /* OCMFunctionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 03F370CA1BAA1DE800CAD3E8 /* OCMFunctionsPrivate.h */; }; - 8BF73E53246CA75E00B9A52C /* OCMNoEscapeBlockTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BF73E52246CA75E00B9A52C /* OCMNoEscapeBlockTests.m */; settings = {COMPILER_FLAGS = "-Xclang -fexperimental-optimized-noescape"; }; }; - 8BF73E54246CA75E00B9A52C /* OCMNoEscapeBlockTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BF73E52246CA75E00B9A52C /* OCMNoEscapeBlockTests.m */; settings = {COMPILER_FLAGS = "-Xclang -fexperimental-optimized-noescape"; }; }; 8B11D4B72448E2E900247BE2 /* OCMCPlusPlus98Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B11D4B62448E2E900247BE2 /* OCMCPlusPlus98Tests.mm */; settings = {COMPILER_FLAGS = "-std=gnu++98"; }; }; 8B11D4B82448E2F400247BE2 /* OCMCPlusPlus98Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B11D4B62448E2E900247BE2 /* OCMCPlusPlus98Tests.mm */; settings = {COMPILER_FLAGS = "-std=gnu++98"; }; }; 8B11D4BA2448E53600247BE2 /* OCMCPlusPlus11Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B11D4B92448E53600247BE2 /* OCMCPlusPlus11Tests.mm */; settings = {COMPILER_FLAGS = "-std=gnu++11"; }; }; 8B11D4BB2448E53600247BE2 /* OCMCPlusPlus11Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8B11D4B92448E53600247BE2 /* OCMCPlusPlus11Tests.mm */; settings = {COMPILER_FLAGS = "-std=gnu++11"; }; }; + 8BF73E53246CA75E00B9A52C /* OCMNoEscapeBlockTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BF73E52246CA75E00B9A52C /* OCMNoEscapeBlockTests.m */; settings = {COMPILER_FLAGS = "-Xclang -fexperimental-optimized-noescape"; }; }; + 8BF73E54246CA75E00B9A52C /* OCMNoEscapeBlockTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BF73E52246CA75E00B9A52C /* OCMNoEscapeBlockTests.m */; settings = {COMPILER_FLAGS = "-Xclang -fexperimental-optimized-noescape"; }; }; 8DE97C5522B43EE60098C63F /* OCMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 03B3159E146333BF0052CD09 /* OCMockObject.m */; }; 8DE97C5622B43EE60098C63F /* OCClassMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 03B3158C146333BF0052CD09 /* OCClassMockObject.m */; }; 8DE97C5722B43EE60098C63F /* OCPartialMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 03B315AA146333BF0052CD09 /* OCPartialMockObject.m */; }; @@ -551,7 +551,7 @@ 2FA28006D043CBDBBAEF6E3F /* OCMMacroState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMMacroState.h; sourceTree = ""; }; 2FA280987F4EA8A4D79000D0 /* OCMMacroState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMMacroState.m; sourceTree = ""; }; 2FA280EB5E8CDEEAE76861F7 /* OCMNonRetainingObjectReturnValueProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMNonRetainingObjectReturnValueProvider.m; sourceTree = ""; }; - 2FA2813F93050582D83E1499 /* OCMockObjectRuntimeTests.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = OCMockObjectRuntimeTests.m; sourceTree = ""; }; + 2FA2813F93050582D83E1499 /* OCMockObjectRuntimeTests.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; path = OCMockObjectRuntimeTests.m; sourceTree = ""; }; 2FA2822E19948FC997965267 /* OCMockObjectTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMockObjectTests.m; sourceTree = ""; }; 2FA2833B48908EAD36444671 /* OCMArgAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMArgAction.h; sourceTree = ""; }; 2FA283D58AA7569D8A5B0C57 /* OCMBlockArgCaller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMBlockArgCaller.m; sourceTree = ""; }; @@ -575,9 +575,9 @@ 3CFBDD751BB3DB200050D9C5 /* TestClassWithCustomReferenceCounting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestClassWithCustomReferenceCounting.h; sourceTree = ""; }; 3CFBDD761BB3DB200050D9C5 /* TestClassWithCustomReferenceCounting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestClassWithCustomReferenceCounting.m; sourceTree = ""; }; 817EB1621BD765130047E85A /* OCMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OCMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8BF73E52246CA75E00B9A52C /* OCMNoEscapeBlockTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMNoEscapeBlockTests.m; sourceTree = ""; }; 8B11D4B62448E2E900247BE2 /* OCMCPlusPlus98Tests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OCMCPlusPlus98Tests.mm; sourceTree = ""; }; 8B11D4B92448E53600247BE2 /* OCMCPlusPlus11Tests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OCMCPlusPlus11Tests.mm; sourceTree = ""; }; + 8BF73E52246CA75E00B9A52C /* OCMNoEscapeBlockTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMNoEscapeBlockTests.m; sourceTree = ""; }; 8DE97CA022B43EE60098C63F /* OCMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OCMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A02926811CA0725A00594AAF /* TestObjects.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TestObjects.xcdatamodel; sourceTree = ""; }; D31108AD1828DB8700737925 /* OCMockLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OCMockLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; From 7932a18ef169fb1139c19d2c6bc42b90d8128aa6 Mon Sep 17 00:00:00 2001 From: Erik Doernenburg Date: Tue, 14 Jul 2020 20:49:58 +0200 Subject: [PATCH 3/3] Made some refactorings. --- Source/OCMock/OCMBoxedReturnValueProvider.m | 45 +++---------------- Source/OCMock/OCMFunctions.m | 18 ++++++++ Source/OCMock/OCMFunctionsPrivate.h | 1 + .../OCMBoxedReturnValueProviderTests.m | 32 +++++-------- Source/OCMockTests/OCMockObjectMacroTests.m | 10 ++--- 5 files changed, 39 insertions(+), 67 deletions(-) diff --git a/Source/OCMock/OCMBoxedReturnValueProvider.m b/Source/OCMock/OCMBoxedReturnValueProvider.m index 43edef02..f28bbc55 100644 --- a/Source/OCMock/OCMBoxedReturnValueProvider.m +++ b/Source/OCMock/OCMBoxedReturnValueProvider.m @@ -18,17 +18,6 @@ #import "OCMFunctionsPrivate.h" #import "NSValue+OCMAdditions.h" -static BOOL IsZeroBuffer(const char* buffer, size_t length) -{ - for(size_t i = 0; i < length; ++i) - { - if(buffer[i] != 0) - { - return NO; - } - } - return YES; -} @implementation OCMBoxedReturnValueProvider @@ -40,10 +29,8 @@ - (void)handleInvocation:(NSInvocation *)anInvocation NSValue *returnValueAsNSValue = (NSValue *)returnValue; [returnValueAsNSValue getValue:valueBuffer]; - if([self isMethodReturnType:returnType - compatibleWithValueType:[returnValueAsNSValue objCType] - value:valueBuffer - valueSize:returnTypeSize]) + if([self isMethodReturnType:returnType compatibleWithValueType:[returnValueAsNSValue objCType] + value:valueBuffer valueSize:returnTypeSize]) { [anInvocation setReturnValue:valueBuffer]; } @@ -58,37 +45,15 @@ - (void)handleInvocation:(NSInvocation *)anInvocation } } -- (BOOL)isMethodReturnType:(const char *)returnType compatibleWithValueType:(const char *)valueType value:(const char*)value valueSize:(size_t)valueSize +- (BOOL)isMethodReturnType:(const char *)returnType compatibleWithValueType:(const char *)valueType value:(const void *)value valueSize:(size_t)valueSize { /* Same types are obviously compatible */ if(strcmp(returnType, valueType) == 0) return YES; - // Special casing for nil and Nil + /* Special treatment for nil and Nil */ if(strcmp(returnType, @encode(id)) == 0 || strcmp(returnType, @encode(Class)) == 0) - { - // Check to verify that the value is actually zero. - if(IsZeroBuffer(value, valueSize)) - { - // nil and Nil get potentially different encodings depending on the compilation - // settings of the file where the return value gets recorded. We check to verify - // against all the values we know of. - const char *validNilEncodings[] = - { - @encode(void *), // Standard Obj C - @encode(int), // 32 bit C++ (before nullptr) - @encode(long long), // 64 bit C++ (before nullptr) - @encode(char *), // C++ with nullptr - }; - for(size_t i = 0; i < sizeof(validNilEncodings) / sizeof(validNilEncodings[0]); ++i) - { - if(strcmp(valueType, validNilEncodings[i]) == 0) - { - return YES; - } - } - } - } + return OCMIsNilValue(valueType, value, valueSize); return OCMEqualTypesAllowingOpaqueStructs(returnType, valueType); } diff --git a/Source/OCMock/OCMFunctions.m b/Source/OCMock/OCMFunctions.m index 5581f484..e493300e 100644 --- a/Source/OCMock/OCMFunctions.m +++ b/Source/OCMock/OCMFunctions.m @@ -296,6 +296,24 @@ BOOL OCMEqualTypesAllowingOpaqueStructs(const char *type1, const char *type2) } } +BOOL OCMIsNilValue(const char *objectCType, const void *value, size_t valueSize) +{ + // First, check value itself + for(size_t i = 0; i < valueSize; i++) + if(((const char *)value)[i] != 0) + return NO; + + // Depending on the compilation settings of the file where the return value gets recorded, + // nil and Nil get potentially different encodings. Check all known encodings. + if((strcmp(objectCType, @encode(void *)) == 0) || // Standard Objective-C + (strcmp(objectCType, @encode(int)) == 0) || // 32 bit C++ (before nullptr) + (strcmp(objectCType, @encode(long long)) == 0) || // 64 bit C++ (before nullptr) + (strcmp(objectCType, @encode(char *)) == 0)) // C++ with nullptr + return YES; + + return NO; +} + BOOL OCMIsAppleBaseClass(Class cls) { diff --git a/Source/OCMock/OCMFunctionsPrivate.h b/Source/OCMock/OCMFunctionsPrivate.h index e1b2231d..d195735a 100644 --- a/Source/OCMock/OCMFunctionsPrivate.h +++ b/Source/OCMock/OCMFunctionsPrivate.h @@ -27,6 +27,7 @@ BOOL OCMIsObjectType(const char *objCType); const char *OCMTypeWithoutQualifiers(const char *objCType); BOOL OCMEqualTypesAllowingOpaqueStructs(const char *type1, const char *type2); CFNumberType OCMNumberTypeForObjCType(const char *objcType); +BOOL OCMIsNilValue(const char *objectCType, const void *value, size_t valueSize); BOOL OCMIsAppleBaseClass(Class cls); BOOL OCMIsApplePrivateMethod(Class cls, SEL sel); diff --git a/Source/OCMockTests/OCMBoxedReturnValueProviderTests.m b/Source/OCMockTests/OCMBoxedReturnValueProviderTests.m index a1273730..288b3b68 100644 --- a/Source/OCMockTests/OCMBoxedReturnValueProviderTests.m +++ b/Source/OCMockTests/OCMBoxedReturnValueProviderTests.m @@ -21,21 +21,13 @@ @interface OCMBoxedReturnValueProvider(Private) - (BOOL)isMethodReturnType:(const char *)returnType compatibleWithValueType:(const char *)valueType value:(const char*)value valueSize:(size_t)valueSize; @end + @interface OCMBoxedReturnValueProviderTests : XCTestCase -{ - char value; - size_t valueSize; -} + @end @implementation OCMBoxedReturnValueProviderTests -- (void)setUp { - [super setUp]; - value = 'A'; - valueSize = 1; -} - - (void)testCorrectEqualityForCppProperty { // see https://github.com/erikdoe/ocmock/issues/96 @@ -62,12 +54,12 @@ - (void)testCorrectEqualityForCppProperty "r^{GURL}"; OCMBoxedReturnValueProvider *boxed = [OCMBoxedReturnValueProvider new]; - XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:&value valueSize:valueSize]); - XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type3 value:&value valueSize:valueSize]); - XCTAssertTrue([boxed isMethodReturnType:type2 compatibleWithValueType:type1 value:&value valueSize:valueSize]); - XCTAssertTrue([boxed isMethodReturnType:type2 compatibleWithValueType:type3 value:&value valueSize:valueSize]); - XCTAssertTrue([boxed isMethodReturnType:type3 compatibleWithValueType:type1 value:&value valueSize:valueSize]); - XCTAssertTrue([boxed isMethodReturnType:type3 compatibleWithValueType:type2 value:&value valueSize:valueSize]); + XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:NULL valueSize:0]); + XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type3 value:NULL valueSize:0]); + XCTAssertTrue([boxed isMethodReturnType:type2 compatibleWithValueType:type1 value:NULL valueSize:0]); + XCTAssertTrue([boxed isMethodReturnType:type2 compatibleWithValueType:type3 value:NULL valueSize:0]); + XCTAssertTrue([boxed isMethodReturnType:type3 compatibleWithValueType:type1 value:NULL valueSize:0]); + XCTAssertTrue([boxed isMethodReturnType:type3 compatibleWithValueType:type2 value:NULL valueSize:0]); } @@ -87,7 +79,7 @@ - (void)testCorrectEqualityForCppReturnTypesWithVtables "ar> >={__rep=(?={__long=QQ*}{__short=(?=Cc)[23c]}{__raw=[3Q]})}}}}"; OCMBoxedReturnValueProvider *boxed = [OCMBoxedReturnValueProvider new]; - XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:&value valueSize:valueSize]); + XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:NULL valueSize:0]); } @@ -98,8 +90,7 @@ - (void)testCorrectEqualityForStructureWithUnknownName const char *type2 = "{CLLocationCoordinate2D=dd}"; OCMBoxedReturnValueProvider *boxed = [OCMBoxedReturnValueProvider new]; - XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:&value valueSize:valueSize]); - + XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:NULL valueSize:0]); } @@ -124,8 +115,7 @@ - (void)testCorrectEqualityForStructureWithoutName "pressed_pair >=^{GURL}}}}"; OCMBoxedReturnValueProvider *boxed = [OCMBoxedReturnValueProvider new]; - XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:&value valueSize:valueSize]); - + XCTAssertTrue([boxed isMethodReturnType:type1 compatibleWithValueType:type2 value:NULL valueSize:0]); } @end diff --git a/Source/OCMockTests/OCMockObjectMacroTests.m b/Source/OCMockTests/OCMockObjectMacroTests.m index 7ab75fec..959596cb 100644 --- a/Source/OCMockTests/OCMockObjectMacroTests.m +++ b/Source/OCMockTests/OCMockObjectMacroTests.m @@ -195,10 +195,7 @@ - (void)testSetsUpStubReturningNilForIdReturnType { id mock = OCMPartialMock([NSArray arrayWithObject:@"Foo"]); - OCMExpect([mock lastObject]).andReturn(nil); - XCTAssertNil([mock lastObject], @"Should have returned stubbed value"); - - OCMExpect([mock lastObject]).andReturn(Nil); + OCMStub([mock lastObject]).andReturn(nil); XCTAssertNil([mock lastObject], @"Should have returned stubbed value"); } @@ -206,10 +203,11 @@ - (void)testSetsUpStubReturningNilForClassReturnType { id mock = OCMPartialMock([[TestClassWithClassReturnMethod alloc] init]); - OCMExpect([mock method]).andReturn(nil); + OCMStub([mock method]).andReturn(Nil); XCTAssertNil([mock method], @"Should have returned stubbed value"); - OCMExpect([mock method]).andReturn(Nil); + // sometimes nil is used where Nil should be used + OCMStub([mock method]).andReturn(nil); XCTAssertNil([mock method], @"Should have returned stubbed value"); }