Last active
August 29, 2015 13:57
-
-
Save nilium/9440566 to your computer and use it in GitHub Desktop.
Switch-case-like test category for NSObject. Uses blocks. Probably not the fastest thing you could do, but handy nonetheless.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static | |
id | |
testObj(id obj) | |
{ | |
return [NSObject testObject: obj, | |
NSNumber.class, ^{ | |
return @"A number"; | |
}, | |
NSString.class, ^{ | |
return @"A string"; | |
}, | |
NSData.class, ^{ | |
return @"A data object"; | |
}, | |
QBlockTest, ^(id obj){ return [obj isKindOfClass:NSObject.class]; }, ^{ | |
return @"It's an object, though."; | |
}, | |
nil, ^{ | |
return @"Not an accepted type"; | |
}]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef NSObject_QTestObject_h_58095849_FEC4_4085_8A11_C6B2C7A2EBFB | |
#define NSObject_QTestObject_h_58095849_FEC4_4085_8A11_C6B2C7A2EBFB | |
#import <Foundation/Foundation.h> | |
extern id const QBlockTest; | |
typedef id (^QTestCase)(); | |
@interface NSObject (QTestObject) | |
+ (id)testObject:(id)rhsObject, ...; | |
@end | |
#endif /* !defined NSObject_QTestObject_h_58095849_FEC4_4085_8A11_C6B2C7A2EBFB */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "NSObject+QTestObject.h" | |
#import <objc/runtime.h> | |
id const QBlockTest = @"__block_string__"; | |
@implementation NSObject (QTestObject) | |
typedef BOOL (^QTestBlock)(id); | |
static | |
BOOL | |
objIsClass(id obj) | |
{ | |
return class_isMetaClass(object_getClass(obj)); | |
} | |
// Static method since it's nice to be able to test against nil on the rhs. | |
/* | |
testObject: tests whether an object is equal to other objects and returns the result of running a | |
case block. Equality is tested either in terms of its pointer, via isEqual:, isEqualToString: (if | |
the lhs responds to isEqualToString: and the rhs is a string -- it will not test against rhs's | |
description since rhs may be nil), or isKindOfClass: if lhs is a class. | |
Variadic arguments are received as a pair of (id, QTestCase) objects. If id is nil, it is the | |
default case and no further tests will occur. If the test case block for the lhsObject is nil, the | |
test ends and the result is nil. The default case may have a block associated with it. | |
Optionally, a case may be the object QBlockTest followed by a block of type BOOL ^test(id) which | |
returns true if the object it receives passes the test. | |
All lhsObjects must have an accompanying QTestCase block. | |
The list of tests must be terminated by a default case (the block may be nil). | |
The rhsObject is always the right-hand side of a comparison except when testing against a Class. | |
This is to avoid calling isEqual: or similar on a nil rhs object, which will always yield zero. | |
So, the comparison will always happen as [lhsObject isEqual:rhsObject] (or isEqualToString:). | |
When testing against a class, the rhsObject's isKindOfClass: method is called if and only if it is | |
not nil. Otherwise, the test fails. | |
Example: | |
NSString *closeitude = [NSObject testObject: @"foobar", | |
@"baz", ^{ return @"close to foobar"; }, | |
@"bar", ^{ return @"closer to foobar"; }, | |
@"foobar", ^{ return @"a foobar"; }, | |
nil, ^{ return @"not a foobar"; }]; // last case tested since lhs is nil | |
*/ | |
+ (id)testObject:(id)rhsObject, ... | |
{ | |
va_list argv; | |
id result = nil; | |
va_start(argv, rhsObject); | |
id lhsObject; | |
QTestBlock lhsTest; | |
QTestCase block; | |
@try { | |
for (;;) { | |
BOOL isBlockTest = false; | |
lhsObject = va_arg(argv, id); | |
// Just test pointer identity here. | |
if ((isBlockTest = (lhsObject == QBlockTest))) { | |
lhsTest = va_arg(argv, QTestBlock); | |
} | |
block = va_arg(argv, QTestCase); | |
if (nil == block) { | |
break; | |
} else if (nil == lhsObject || lhsObject == rhsObject) { | |
// Pointer identity test / default case | |
goto q_matching_test_case; | |
} else if (rhsObject && objIsClass(lhsObject)) { | |
if ([rhsObject isKindOfClass:lhsObject]) { | |
goto q_matching_test_case; | |
} | |
} else if (isBlockTest) { | |
if (lhsTest(rhsObject)) { | |
goto q_matching_test_case; | |
} | |
} else if ([lhsObject respondsToSelector:@selector(isEqualToString:)] && | |
[rhsObject isKindOfClass:NSString.class]) { | |
// Nested if to skip next condition | |
if ([lhsObject isEqualToString:rhsObject]) { | |
goto q_matching_test_case; | |
} | |
} else if ([lhsObject isEqual:rhsObject]) { | |
// Otherwise, try to compare the two objects | |
q_matching_test_case: | |
result = block(); | |
break; | |
} | |
} | |
} @finally { | |
va_end(argv); | |
} | |
return result; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment