Skip to content

Commit

Permalink
Add a mechanism for boxing nil arguments to prevent an exception when
Browse files Browse the repository at this point in the history
generating an array of arguments

#306
[#86329706]
  • Loading branch information
Brian Croom committed Jan 17, 2015
1 parent 0d8a7ec commit 01b7c35
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 7 deletions.
14 changes: 14 additions & 0 deletions Cedar.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@
34D4B5C318F3AE0400FB2C3B /* UIKitContainSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34D4B5C118F3ADFF00FB2C3B /* UIKitContainSpec.mm */; };
34D93AD51911441300200C71 /* DeallocNotifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 343FAFE9190FDAEC0085AFEC /* DeallocNotifier.m */; };
34EBFD0F18FF505F005392AB /* UIKitComparatorsContainer.h in Copy headers to framework */ = {isa = PBXBuildFile; fileRef = 34D4B5C418F3B68900FB2C3B /* UIKitComparatorsContainer.h */; };
34F3DF7F1A6ABA2E003041DA /* CDRNil.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F3DF7C1A6ABA2E003041DA /* CDRNil.m */; };
34F3DF801A6ABA2E003041DA /* CDRNil.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F3DF7C1A6ABA2E003041DA /* CDRNil.m */; };
34F3DF821A6ABB21003041DA /* CDRNilSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34F3DF811A6ABB21003041DA /* CDRNilSpec.mm */; };
34F3DF831A6ABB21003041DA /* CDRNilSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34F3DF811A6ABB21003041DA /* CDRNilSpec.mm */; };
42064466139B44EC00C85605 /* CDRTeamCityReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 42064465139B44EC00C85605 /* CDRTeamCityReporter.h */; settings = {ATTRIBUTES = (Public, ); }; };
42064467139B44EC00C85605 /* CDRTeamCityReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 42064465139B44EC00C85605 /* CDRTeamCityReporter.h */; };
4206446A139B44F600C85605 /* CDRTeamCityReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 42064469139B44F600C85605 /* CDRTeamCityReporter.m */; };
Expand Down Expand Up @@ -709,6 +713,9 @@
34D1E67A18F7A2E6005161AD /* AnInstanceOf.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AnInstanceOf.h; path = ../Comparators/AnInstanceOf.h; sourceTree = "<group>"; };
34D4B5C118F3ADFF00FB2C3B /* UIKitContainSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = UIKitContainSpec.mm; sourceTree = "<group>"; };
34D4B5C418F3B68900FB2C3B /* UIKitComparatorsContainer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UIKitComparatorsContainer.h; sourceTree = "<group>"; };
34F3DF7B1A6ABA2E003041DA /* CDRNil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDRNil.h; sourceTree = "<group>"; };
34F3DF7C1A6ABA2E003041DA /* CDRNil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDRNil.m; sourceTree = "<group>"; };
34F3DF811A6ABB21003041DA /* CDRNilSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CDRNilSpec.mm; sourceTree = "<group>"; };
42064465139B44EC00C85605 /* CDRTeamCityReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDRTeamCityReporter.h; sourceTree = "<group>"; };
42064469139B44F600C85605 /* CDRTeamCityReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDRTeamCityReporter.m; sourceTree = "<group>"; };
4523F16026FC3298AB3B00BE /* ExampleWithPublicRunDates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExampleWithPublicRunDates.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1598,6 +1605,7 @@
AEEE1FE611DC27B800029872 /* CDRSpecHelper.m */,
969B6F82160C61E000C7C792 /* CDRSymbolicator.m */,
34681C2B18FE451E009D38AC /* CDRTypeUtilities.m */,
34F3DF7C1A6ABA2E003041DA /* CDRNil.m */,
);
path = Source;
sourceTree = "<group>";
Expand Down Expand Up @@ -1627,6 +1635,7 @@
3460489318F2DBBF00BC93B6 /* CDRBlockHelper.h */,
34681C2D18FE4611009D38AC /* CDRTypeUtilities.h */,
AE55BF1D19A7CF83005948E6 /* CDRRuntimeUtilities.h */,
34F3DF7B1A6ABA2E003041DA /* CDRNil.h */,
);
path = Headers;
sourceTree = "<group>";
Expand Down Expand Up @@ -1672,6 +1681,7 @@
AEEE1FF211DC27B800029872 /* SpecSpec2.m */,
AEEE1FEF11DC27B800029872 /* main.m */,
34681C2F18FE4B68009D38AC /* CDRTypeUtilitiesSpec.mm */,
34F3DF811A6ABB21003041DA /* CDRNilSpec.mm */,
);
path = Spec;
sourceTree = "<group>";
Expand Down Expand Up @@ -2520,6 +2530,7 @@
34ADD2E01921F18100B057AC /* AnyInstanceOfClassArgument.mm in Sources */,
AE31A2A119C0F23F00C438C1 /* CDRXTestSuite.m in Sources */,
AEEE1FF411DC27B800029872 /* CDRExample.m in Sources */,
34F3DF7F1A6ABA2E003041DA /* CDRNil.m in Sources */,
AEEE1FF511DC27B800029872 /* CDRExampleBase.m in Sources */,
AEEE1FF611DC27B800029872 /* CDRExampleGroup.m in Sources */,
AEEE1FF711DC27B800029872 /* CDRFunctions.m in Sources */,
Expand Down Expand Up @@ -2597,6 +2608,7 @@
AE18A80A13F4640600C8872C /* ContainSpec.mm in Sources */,
AE6F3F341458D7C100C98F1E /* BeGreaterThanSpec.mm in Sources */,
AEF33009145B4E3B002F93BB /* BeGTESpec.mm in Sources */,
34F3DF821A6ABB21003041DA /* CDRNilSpec.mm in Sources */,
AEF3301C145B62E9002F93BB /* BeLessThanSpec.mm in Sources */,
AEF3301E145B68D7002F93BB /* BeLTESpec.mm in Sources */,
966E74ED145A6CA0002E8D49 /* ShouldSyntaxSpec.mm in Sources */,
Expand Down Expand Up @@ -2642,6 +2654,7 @@
AEEE223511DC2B6500029872 /* CDRFunctions.m in Sources */,
AEEE223611DC2B6500029872 /* CDRSpec.m in Sources */,
AEEE223711DC2B6500029872 /* CDRSpecHelper.m in Sources */,
34F3DF801A6ABA2E003041DA /* CDRNil.m in Sources */,
AE0C9D8F19C0C64200B4DD2B /* CDRSpec+XCTestSupport.m in Sources */,
AEEE223D11DC2B6D00029872 /* CedarApplicationDelegate.m in Sources */,
AE55BF1C19A7CF58005948E6 /* CDRRuntimeUtilities.m in Sources */,
Expand Down Expand Up @@ -2716,6 +2729,7 @@
AEF7302D13ECC4E700786282 /* BeEmptySpec.mm in Sources */,
228F3FA717E3ECD10000C8AF /* CDRSpyiOSSpec.mm in Sources */,
44B9A71F1888661100CBCA1B /* ExampleWithPublicRunDates.m in Sources */,
34F3DF831A6ABB21003041DA /* CDRNilSpec.mm in Sources */,
AE18A80B13F4640600C8872C /* ContainSpec.mm in Sources */,
AED10EBD18F46C0E00950904 /* FooSuperclass.m in Sources */,
343FAFEB190FDAEC0085AFEC /* DeallocNotifier.m in Sources */,
Expand Down
21 changes: 21 additions & 0 deletions Source/CDRNil.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#import "CDRNil.h"

@implementation CDRNil

+ (instancetype)nilObject {
return [[[self alloc] init] autorelease];
}

- (BOOL)isEqual:(id)object {
return object==self || [object isMemberOfClass:[self class]];
}

- (NSString *)description {
return @"<nil>";
}

- (id)copyWithZone:(NSZone *)zone {
return [self retain];
}

@end
3 changes: 2 additions & 1 deletion Source/CDRTypeUtilities.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "CDRTypeUtilities.h"
#import "CDRNil.h"

@implementation CDRTypeUtilities

Expand Down Expand Up @@ -134,7 +135,7 @@ + (id)boxedObjectOfBytes:(const char *)argBuffer ofObjCType:(const char *)argTyp
memcpy(&i, argBuffer, sizeof(TYPE)); \
i;})
if (IS_TYPE(id)) {
return (id)*((void **)argBuffer);
return (id)*((void **)argBuffer) ?: [CDRNil nilObject];
} else if (IS_TYPE(Class)) {
return (id)*((void **)argBuffer);
} else if (IS_TYPE(void(^)())) {
Expand Down
12 changes: 12 additions & 0 deletions Source/Headers/CDRNil.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>

/**
* CDRNil is an internal class used to box 'nil' values when they need to be put into a
* Cocoa collection. This is needed beside NSNull to allow differentiating between usages
* of NSNull and true nils.
*/
@interface CDRNil : NSObject <NSCopying>

+ (instancetype)nilObject;

@end
31 changes: 31 additions & 0 deletions Spec/CDRNilSpec.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#import <Cedar/Cedar.h>
#import "CDRNil.h"

using namespace Cedar::Matchers;
using namespace Cedar::Doubles;

SPEC_BEGIN(CDRNilSpec)

describe(@"CDRNil", ^{
__block CDRNil *nilObject;

beforeEach(^{
nilObject = [CDRNil nilObject];
});

it(@"should return itself when copied", ^{
CDRNil *copiedNil = [[nilObject copy] autorelease];
copiedNil should be_same_instance_as(nilObject);
});

it(@"should be equal to other instances of CDRNil", ^{
CDRNil *anotherNil = [CDRNil nilObject];
anotherNil should equal(nilObject);
});

it(@"should have a clear description indicating what it represents", ^{
[nilObject description] should equal(@"<nil>");
});
});

SPEC_END
6 changes: 6 additions & 0 deletions Spec/CDRTypeUtilitiesSpec.mm
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#import <Cedar/CDRSpecHelper.h>
#import "CDRTypeUtilities.h"
#import "CDRNil.h"

using namespace Cedar::Matchers;
using namespace Cedar::Doubles;
Expand Down Expand Up @@ -83,6 +84,11 @@
[CDRTypeUtilities boxedObjectOfBytes:(const char *)&foo ofObjCType:@encode(id)] should be_same_instance_as(foo);
});

it(@"should return CDRNil when given nil", ^{
id nilParam = nil;
[CDRTypeUtilities boxedObjectOfBytes:(const char *)&nilParam ofObjCType:@encode(id)] should equal([CDRNil nilObject]);
});

it(@"should return the class it was given", ^{
Class aClass = [NSObject class];
[CDRTypeUtilities boxedObjectOfBytes:(const char *)&aClass ofObjCType:@encode(Class)] should equal(aClass);
Expand Down
14 changes: 8 additions & 6 deletions Spec/Doubles/HaveReceivedSpec.mm
Original file line number Diff line number Diff line change
Expand Up @@ -469,27 +469,29 @@
});
});

context(@"which has been called with a nil parameter", ^{
context(@"which has been called with an incorrect nil parameter", ^{
NSObject * expectedSecondParameter = @666;

beforeEach(^{
[incrementer incrementByABit:actualFirstParameter andABitMore:nil];
});

describe(@"positive match", ^{
it(@"should fail with a sensible error message", ^{
expectFailureWithMessage([NSString stringWithFormat:@"Expected <%@> to have received message <%@>, with arguments: <%d, %@> but received messages:\n"
@" incrementByABit:andABitMore:<83, 32>\n"
expectFailureWithMessage([NSString stringWithFormat:@"Expected <%@> to have received message <%@>, with arguments: <%d, 666> but received messages:\n"
@" incrementByABit:andABitMore:<83, %@>\n"
@" value\n"
@" setValue:<115>\n",
@" setValue:<83>\n",
incrementer, NSStringFromSelector(method), actualFirstParameter, @"<nil>"], ^{
expect(incrementer).to(have_received("incrementByABit:andABitMore:").with(actualFirstParameter, @666));
expect(incrementer).to(have_received("incrementByABit:andABitMore:").with(actualFirstParameter, expectedSecondParameter));
});
});
});

describe(@"negative match", ^{
it(@"should pass", ^{
expect(incrementer).to_not(have_received(method).with(0, nil));
expect(incrementer).to_not(have_received("incrementByABit:andABitMore:").with(actualFirstParameter, @667));
expect(incrementer).to_not(have_received("incrementByABit:andABitMore:").with(actualFirstParameter, expectedSecondParameter));
});
});
});
Expand Down

0 comments on commit 01b7c35

Please sign in to comment.