no message

This commit is contained in:
giuseppenuc committed 2015-12-31 16:06:21 +01:00
1 parent de5bd21a43
commit 568ad5c409
262 files changed
+23226 -78

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
//
// EXPRuntimeMatcher.h
// Expecta
//
// Created by Luke Redpath on 26/03/2012.
// Copyright (c) 2012 Peter Jihoon Kim. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "EXPMatcher.h"
#import "EXPDefines.h"
@interface EXPBlockDefinedMatcher : NSObject <EXPMatcher> {
EXPBoolBlock prerequisiteBlock;
EXPBoolBlock matchBlock;
EXPStringBlock failureMessageForToBlock;
EXPStringBlock failureMessageForNotToBlock;
}
@property(nonatomic, copy) EXPBoolBlock prerequisiteBlock;
@property(nonatomic, copy) EXPBoolBlock matchBlock;
@property(nonatomic, copy) EXPStringBlock failureMessageForToBlock;
@property(nonatomic, copy) EXPStringBlock failureMessageForNotToBlock;
@end
+60
View File
@@ -0,0 +1,60 @@
//
// EXPRuntimeMatcher.m
// Expecta
//
// Created by Luke Redpath on 26/03/2012.
// Copyright (c) 2012 Peter Jihoon Kim. All rights reserved.
//
#import "EXPBlockDefinedMatcher.h"
@implementation EXPBlockDefinedMatcher
- (void)dealloc
{
self.prerequisiteBlock = nil;
self.matchBlock = nil;
self.failureMessageForToBlock = nil;
self.failureMessageForNotToBlock = nil;
[super dealloc];
}
@synthesize prerequisiteBlock;
@synthesize matchBlock;
@synthesize failureMessageForToBlock;
@synthesize failureMessageForNotToBlock;
- (BOOL)meetsPrerequesiteFor:(id)actual
{
if (self.prerequisiteBlock) {
return self.prerequisiteBlock();
}
return YES;
}
- (BOOL)matches:(id)actual
{
if (self.matchBlock) {
return self.matchBlock();
}
return YES;
}
- (NSString *)failureMessageForTo:(id)actual
{
if (self.failureMessageForToBlock) {
return self.failureMessageForToBlock();
}
return nil;
}
- (NSString *)failureMessageForNotTo:(id)actual
{
if (self.failureMessageForNotToBlock) {
return self.failureMessageForNotToBlock();
}
return nil;
}
@end
+17
View File
@@ -0,0 +1,17 @@
//
// EXPDefines.h
// Expecta
//
// Created by Luke Redpath on 26/03/2012.
// Copyright (c) 2012 Peter Jihoon Kim. All rights reserved.
//
#ifndef Expecta_EXPDefines_h
#define Expecta_EXPDefines_h
typedef void (^EXPBasicBlock)();
typedef id (^EXPIdBlock)();
typedef BOOL (^EXPBoolBlock)();
typedef NSString *(^EXPStringBlock)();
#endif
+13
View File
@@ -0,0 +1,13 @@
#import <Foundation/Foundation.h>
@interface EXPDoubleTuple : NSObject {
double *_values;
size_t _size;
}
@property (nonatomic, assign) double *values;
@property (nonatomic, assign) size_t size;
- (instancetype)initWithDoubleValues:(double *)values size:(size_t)size NS_DESIGNATED_INITIALIZER;
@end
+45
View File
@@ -0,0 +1,45 @@
#import "EXPDoubleTuple.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
@implementation EXPDoubleTuple
#pragma clang diagnostic pop
@synthesize values = _values, size = _size;
- (instancetype)initWithDoubleValues:(double *)values size:(size_t)size {
if ((self = [super init])) {
self.values = malloc(sizeof(double) * size);
memcpy(self.values, values, sizeof(double) * size);
self.size = size;
}
return self;
}
- (void)dealloc {
free(self.values);
[super dealloc];
}
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[EXPDoubleTuple class]]) return NO;
EXPDoubleTuple *other = (EXPDoubleTuple *)object;
if (self.size == other.size) {
for (int i = 0; i < self.size; ++i) {
if (self.values[i] != other.values[i]) return NO;
}
return YES;
}
return NO;
}
- (NSString *)description {
if (self.size == 2) {
return [NSString stringWithFormat:@"Double tuple: {%f, %f}", self.values[0], self.values[1]];
} else if (self.size == 4) {
return [NSString stringWithFormat:@"Double tuple: {%f, %f, %f, %f}", self.values[0], self.values[1], self.values[2], self.values[3]];
}
return [NSString stringWithFormat:@"Double tuple of unexpected size %zd, sadly", self.size];
}
@end
+45
View File
@@ -0,0 +1,45 @@
#import <Foundation/Foundation.h>
#import "EXPMatcher.h"
#import "EXPDefines.h"
@interface EXPExpect : NSObject {
EXPIdBlock _actualBlock;
id _testCase;
int _lineNumber;
char *_fileName;
BOOL _negative;
BOOL _asynchronous;
NSTimeInterval _timeout;
}
@property(nonatomic, copy) EXPIdBlock actualBlock;
@property(nonatomic, readonly) id actual;
@property(nonatomic, assign) id testCase;
@property(nonatomic) int lineNumber;
@property(nonatomic) const char *fileName;
@property(nonatomic) BOOL negative;
@property(nonatomic) BOOL asynchronous;
@property(nonatomic) NSTimeInterval timeout;
@property(nonatomic, readonly) EXPExpect *to;
@property(nonatomic, readonly) EXPExpect *toNot;
@property(nonatomic, readonly) EXPExpect *notTo;
@property(nonatomic, readonly) EXPExpect *will;
@property(nonatomic, readonly) EXPExpect *willNot;
@property(nonatomic, readonly) EXPExpect *(^after)(NSTimeInterval timeInterval);
- (instancetype)initWithActualBlock:(id)actualBlock testCase:(id)testCase lineNumber:(int)lineNumber fileName:(const char *)fileName NS_DESIGNATED_INITIALIZER;
+ (EXPExpect *)expectWithActualBlock:(id)actualBlock testCase:(id)testCase lineNumber:(int)lineNumber fileName:(const char *)fileName;
- (void)applyMatcher:(id<EXPMatcher>)matcher;
- (void)applyMatcher:(id<EXPMatcher>)matcher to:(NSObject **)actual;
@end
@interface EXPDynamicPredicateMatcher : NSObject <EXPMatcher> {
EXPExpect *_expectation;
SEL _selector;
}
- (instancetype)initWithExpectation:(EXPExpect *)expectation selector:(SEL)selector NS_DESIGNATED_INITIALIZER;
@property (nonatomic, readonly, copy) void (^dispatch)(void);
@end
+221
View File
@@ -0,0 +1,221 @@
#import "EXPExpect.h"
#import "NSObject+Expecta.h"
#import "Expecta.h"
#import "EXPUnsupportedObject.h"
#import "EXPMatcher.h"
#import "EXPBlockDefinedMatcher.h"
#import <libkern/OSAtomic.h>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
@implementation EXPExpect
#pragma clang diagnostic pop
@dynamic
actual,
to,
toNot,
notTo,
will,
willNot,
after;
@synthesize
actualBlock=_actualBlock,
testCase=_testCase,
negative=_negative,
asynchronous=_asynchronous,
timeout=_timeout,
lineNumber=_lineNumber,
fileName=_fileName;
- (instancetype)initWithActualBlock:(id)actualBlock testCase:(id)testCase lineNumber:(int)lineNumber fileName:(const char *)fileName {
self = [super init];
if(self) {
self.actualBlock = actualBlock;
self.testCase = testCase;
self.negative = NO;
self.asynchronous = NO;
self.timeout = [Expecta asynchronousTestTimeout];
self.lineNumber = lineNumber;
self.fileName = fileName;
}
return self;
}
- (void)dealloc
{
[_actualBlock release];
_actualBlock = nil;
[super dealloc];
}
+ (EXPExpect *)expectWithActualBlock:(id)actualBlock testCase:(id)testCase lineNumber:(int)lineNumber fileName:(const char *)fileName {
return [[[EXPExpect alloc] initWithActualBlock:actualBlock testCase:(id)testCase lineNumber:lineNumber fileName:fileName] autorelease];
}
#pragma mark -
- (EXPExpect *)to {
return self;
}
- (EXPExpect *)toNot {
self.negative = !self.negative;
return self;
}
- (EXPExpect *)notTo {
return [self toNot];
}
- (EXPExpect *)will {
self.asynchronous = YES;
return self;
}
- (EXPExpect *)willNot {
return self.will.toNot;
}
- (EXPExpect *(^)(NSTimeInterval))after
{
EXPExpect * (^block)(NSTimeInterval) = [^EXPExpect *(NSTimeInterval timeout) {
self.asynchronous = YES;
self.timeout = timeout;
return self;
} copy];
return [block autorelease];
}
#pragma mark -
- (id)actual {
if(self.actualBlock) {
return self.actualBlock();
}
return nil;
}
- (void)applyMatcher:(id<EXPMatcher>)matcher
{
id actual = [self actual];
[self applyMatcher:matcher to:&actual];
}
- (void)applyMatcher:(id<EXPMatcher>)matcher to:(NSObject **)actual {
if([*actual isKindOfClass:[EXPUnsupportedObject class]]) {
EXPFail(self.testCase, self.lineNumber, self.fileName,
[NSString stringWithFormat:@"expecting a %@ is not supported", ((EXPUnsupportedObject *)*actual).type]);
} else {
BOOL failed = NO;
if([matcher respondsToSelector:@selector(meetsPrerequesiteFor:)] &&
![matcher meetsPrerequesiteFor:*actual]) {
failed = YES;
} else {
BOOL matchResult = NO;
if(self.asynchronous) {
NSTimeInterval timeOut = self.timeout;
NSDate *expiryDate = [NSDate dateWithTimeIntervalSinceNow:timeOut];
while(1) {
matchResult = [matcher matches:*actual];
failed = self.negative ? matchResult : !matchResult;
if(!failed || ([(NSDate *)[NSDate date] compare:expiryDate] == NSOrderedDescending)) {
break;
}
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
OSMemoryBarrier();
*actual = self.actual;
}
} else {
matchResult = [matcher matches:*actual];
}
failed = self.negative ? matchResult : !matchResult;
}
if(failed) {
NSString *message = nil;
if(self.negative) {
if ([matcher respondsToSelector:@selector(failureMessageForNotTo:)]) {
message = [matcher failureMessageForNotTo:*actual];
}
} else {
if ([matcher respondsToSelector:@selector(failureMessageForTo:)]) {
message = [matcher failureMessageForTo:*actual];
}
}
if (message == nil) {
message = @"Match Failed.";
}
EXPFail(self.testCase, self.lineNumber, self.fileName, message);
}
}
self.negative = NO;
}
#pragma mark - Dynamic predicate dispatch
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if ([self.actual respondsToSelector:aSelector]) {
return [self.actual methodSignatureForSelector:aSelector];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([self.actual respondsToSelector:anInvocation.selector]) {
EXPDynamicPredicateMatcher *matcher = [[EXPDynamicPredicateMatcher alloc] initWithExpectation:self selector:anInvocation.selector];
[anInvocation setSelector:@selector(dispatch)];
[anInvocation invokeWithTarget:matcher];
[matcher release];
}
else {
[super forwardInvocation:anInvocation];
}
}
@end
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
@implementation EXPDynamicPredicateMatcher
#pragma clang diagnostic pop
- (instancetype)initWithExpectation:(EXPExpect *)expectation selector:(SEL)selector
{
if ((self = [super init])) {
_expectation = expectation;
_selector = selector;
}
return self;
}
- (BOOL)matches:(id)actual
{
return (BOOL)[actual performSelector:_selector];
}
- (NSString *)failureMessageForTo:(id)actual
{
return [NSString stringWithFormat:@"expected %@ to be true", NSStringFromSelector(_selector)];
}
- (NSString *)failureMessageForNotTo:(id)actual
{
return [NSString stringWithFormat:@"expected %@ to be false", NSStringFromSelector(_selector)];
}
- (void (^)(void))dispatch
{
__block id blockExpectation = _expectation;
return [[^{
[blockExpectation applyMatcher:self];
} copy] autorelease];
}
@end
+13
View File
@@ -0,0 +1,13 @@
#import <Foundation/Foundation.h>
@interface EXPFloatTuple : NSObject {
float *_values;
size_t _size;
}
@property (nonatomic, assign) float *values;
@property (nonatomic, assign) size_t size;
- (instancetype)initWithFloatValues:(float *)values size:(size_t)size NS_DESIGNATED_INITIALIZER;
@end
+55
View File
@@ -0,0 +1,55 @@
#import "EXPFloatTuple.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
@implementation EXPFloatTuple
#pragma clang diagnostic pop
@synthesize values = _values, size = _size;
- (instancetype)initWithFloatValues:(float *)values size:(size_t)size {
if ((self = [super init])) {
self.values = malloc(sizeof(float) * size);
memcpy(self.values, values, sizeof(float) * size);
self.size = size;
}
return self;
}
- (void)dealloc {
free(self.values);
[super dealloc];
}
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[EXPFloatTuple class]]) return NO;
EXPFloatTuple *other = (EXPFloatTuple *)object;
if (self.size == other.size) {
for (int i = 0; i < self.size; ++i) {
if (self.values[i] != other.values[i]) return NO;
}
return YES;
}
return NO;
}
- (NSUInteger)hash
{
NSUInteger prime = 31;
NSUInteger hash = 0;
for (int i=0; i<self.size; i++) {
hash = prime * hash + (NSUInteger)self.values[i];
}
return hash;
}
- (NSString *)description {
if (self.size == 2) {
return [NSString stringWithFormat:@"Float tuple: {%f, %f}", self.values[0], self.values[1]];
} else if (self.size == 4) {
return [NSString stringWithFormat:@"Float tuple: {%f, %f, %f, %f}", self.values[0], self.values[1], self.values[2], self.values[3]];
}
return [NSString stringWithFormat:@"Float tuple of unexpected size %zd, sadly", self.size];
}
@end
+20
View File
@@ -0,0 +1,20 @@
//
// EXPMatcher.h
// Expecta
//
// Created by Luke Redpath on 26/03/2012.
// Copyright (c) 2012 Peter Jihoon Kim. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol EXPMatcher <NSObject>
- (BOOL)matches:(id)actual;
@optional
- (BOOL)meetsPrerequesiteFor:(id)actual;
- (NSString *)failureMessageForTo:(id)actual;
- (NSString *)failureMessageForNotTo:(id)actual;
@end
+11
View File
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@interface EXPUnsupportedObject : NSObject {
NSString *_type;
}
@property (nonatomic, retain) NSString *type;
- (instancetype)initWithType:(NSString *)type NS_DESIGNATED_INITIALIZER;
@end
+23
View File
@@ -0,0 +1,23 @@
#import "EXPUnsupportedObject.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
@implementation EXPUnsupportedObject
#pragma clang diagnostic pop
@synthesize type=_type;
- (instancetype)initWithType:(NSString *)type {
self = [super init];
if(self) {
self.type = type;
}
return self;
}
- (void)dealloc {
self.type = nil;
[super dealloc];
}
@end
+15
View File
@@ -0,0 +1,15 @@
#import <Foundation/Foundation.h>
//! Project version number for Expecta.
FOUNDATION_EXPORT double ExpectaVersionNumber;
//! Project version string for Expecta.
FOUNDATION_EXPORT const unsigned char ExpectaVersionString[];
#import <Expecta/ExpectaObject.h>
#import <Expecta/ExpectaSupport.h>
#import <Expecta/EXPMatchers.h>
// Enable shorthand by default
#define expect(...) EXP_expect((__VA_ARGS__))
#define failure(...) EXP_failure((__VA_ARGS__))
+18
View File
@@ -0,0 +1,18 @@
#import <Foundation/Foundation.h>
#define EXPObjectify(value) _EXPObjectify(@encode(__typeof__((value))), (value))
#define EXP_expect(actual) _EXP_expect(self, __LINE__, __FILE__, ^id{ __typeof__((actual)) strongActual = (actual); return EXPObjectify(strongActual); })
#define EXPMatcherInterface(matcherName, matcherArguments) _EXPMatcherInterface(matcherName, matcherArguments)
#define EXPMatcherImplementationBegin(matcherName, matcherArguments) _EXPMatcherImplementationBegin(matcherName, matcherArguments)
#define EXPMatcherImplementationEnd _EXPMatcherImplementationEnd
#define EXPMatcherAliasImplementation(newMatcherName, oldMatcherName, matcherArguments) _EXPMatcherAliasImplementation(newMatcherName, oldMatcherName, matcherArguments)
#define EXP_failure(message) EXPFail(self, __LINE__, __FILE__, message)
@interface Expecta : NSObject
+ (NSTimeInterval)asynchronousTestTimeout;
+ (void)setAsynchronousTestTimeout:(NSTimeInterval)timeout;
@end
+15
View File
@@ -0,0 +1,15 @@
#import "ExpectaObject.h"
@implementation Expecta
static NSTimeInterval _asynchronousTestTimeout = 1.0;
+ (NSTimeInterval)asynchronousTestTimeout {
return _asynchronousTestTimeout;
}
+ (void)setAsynchronousTestTimeout:(NSTimeInterval)timeout {
_asynchronousTestTimeout = timeout;
}
@end
+74
View File
@@ -0,0 +1,74 @@
#import "EXPExpect.h"
#import "EXPBlockDefinedMatcher.h"
#ifdef __cplusplus
extern "C" {
#endif
id _EXPObjectify(const char *type, ...);
EXPExpect *_EXP_expect(id testCase, int lineNumber, const char *fileName, EXPIdBlock actualBlock);
void EXPFail(id testCase, int lineNumber, const char *fileName, NSString *message);
NSString *EXPDescribeObject(id obj);
void EXP_prerequisite(EXPBoolBlock block);
void EXP_match(EXPBoolBlock block);
void EXP_failureMessageForTo(EXPStringBlock block);
void EXP_failureMessageForNotTo(EXPStringBlock block);
#if __has_feature(objc_arc)
#define _EXP_release(x)
#define _EXP_autorelease(x) (x)
#else
#define _EXP_release(x) [x release]
#define _EXP_autorelease(x) [x autorelease]
#endif
// workaround for the categories bug: http://developer.apple.com/library/mac/#qa/qa1490/_index.html
#define EXPFixCategoriesBug(name) \
__attribute__((constructor)) static void EXPFixCategoriesBug##name() {}
#define _EXPMatcherInterface(matcherName, matcherArguments) \
@interface EXPExpect (matcherName##Matcher) \
@property (nonatomic, readonly) void(^ matcherName) matcherArguments; \
@end
#define _EXPMatcherImplementationBegin(matcherName, matcherArguments) \
EXPFixCategoriesBug(EXPMatcher##matcherName##Matcher); \
@implementation EXPExpect (matcherName##Matcher) \
@dynamic matcherName;\
- (void(^) matcherArguments) matcherName { \
EXPBlockDefinedMatcher *matcher = [[EXPBlockDefinedMatcher alloc] init]; \
[[[NSThread currentThread] threadDictionary] setObject:matcher forKey:@"EXP_currentMatcher"]; \
__block id actual = self.actual; \
__block void (^prerequisite)(EXPBoolBlock block) = ^(EXPBoolBlock block) { EXP_prerequisite(block); }; \
__block void (^match)(EXPBoolBlock block) = ^(EXPBoolBlock block) { EXP_match(block); }; \
__block void (^failureMessageForTo)(EXPStringBlock block) = ^(EXPStringBlock block) { EXP_failureMessageForTo(block); }; \
__block void (^failureMessageForNotTo)(EXPStringBlock block) = ^(EXPStringBlock block) { EXP_failureMessageForNotTo(block); }; \
prerequisite(nil); match(nil); failureMessageForTo(nil); failureMessageForNotTo(nil); \
void (^matcherBlock) matcherArguments = [^ matcherArguments { \
{
#define _EXPMatcherImplementationEnd \
} \
[self applyMatcher:matcher to:&actual]; \
[[[NSThread currentThread] threadDictionary] removeObjectForKey:@"EXP_currentMatcher"]; \
} copy]; \
_EXP_release(matcher); \
return _EXP_autorelease(matcherBlock); \
} \
@end
#define _EXPMatcherAliasImplementation(newMatcherName, oldMatcherName, matcherArguments) \
EXPFixCategoriesBug(EXPMatcher##newMatcherName##Matcher); \
@implementation EXPExpect (newMatcherName##Matcher) \
@dynamic newMatcherName;\
- (void(^) matcherArguments) newMatcherName { \
return [self oldMatcherName]; \
}\
@end
#ifdef __cplusplus
}
#endif
+176
View File
@@ -0,0 +1,176 @@
#import "ExpectaSupport.h"
#import "NSValue+Expecta.h"
#import "NSObject+Expecta.h"
#import "EXPUnsupportedObject.h"
#import "EXPFloatTuple.h"
#import "EXPDoubleTuple.h"
#import "EXPDefines.h"
#import <objc/runtime.h>
@interface NSObject (ExpectaXCTestRecordFailure)
// suppress warning
- (void)recordFailureWithDescription:(NSString *)description inFile:(NSString *)filename atLine:(NSUInteger)lineNumber expected:(BOOL)expected;
@end
id _EXPObjectify(const char *type, ...) {
va_list v;
va_start(v, type);
id obj = nil;
if(strcmp(type, @encode(char)) == 0) {
char actual = (char)va_arg(v, int);
obj = @(actual);
} else if(strcmp(type, @encode(_Bool)) == 0) {
_Static_assert(sizeof(_Bool) <= sizeof(int), "Expected _Bool to be subject to vararg type promotion");
_Bool actual = (_Bool)va_arg(v, int);
obj = @(actual);
} else if(strcmp(type, @encode(double)) == 0) {
double actual = (double)va_arg(v, double);
obj = @(actual);
} else if(strcmp(type, @encode(float)) == 0) {
float actual = (float)va_arg(v, double);
obj = @(actual);
} else if(strcmp(type, @encode(int)) == 0) {
int actual = (int)va_arg(v, int);
obj = @(actual);
} else if(strcmp(type, @encode(long)) == 0) {
long actual = (long)va_arg(v, long);
obj = @(actual);
} else if(strcmp(type, @encode(long long)) == 0) {
long long actual = (long long)va_arg(v, long long);
obj = @(actual);
} else if(strcmp(type, @encode(short)) == 0) {
short actual = (short)va_arg(v, int);
obj = @(actual);
} else if(strcmp(type, @encode(unsigned char)) == 0) {
unsigned char actual = (unsigned char)va_arg(v, unsigned int);
obj = @(actual);
} else if(strcmp(type, @encode(unsigned int)) == 0) {
unsigned int actual = (int)va_arg(v, unsigned int);
obj = @(actual);
} else if(strcmp(type, @encode(unsigned long)) == 0) {
unsigned long actual = (unsigned long)va_arg(v, unsigned long);
obj = @(actual);
} else if(strcmp(type, @encode(unsigned long long)) == 0) {
unsigned long long actual = (unsigned long long)va_arg(v, unsigned long long);
obj = @(actual);
} else if(strcmp(type, @encode(unsigned short)) == 0) {
unsigned short actual = (unsigned short)va_arg(v, unsigned int);
obj = @(actual);
} else if(strstr(type, @encode(EXPBasicBlock)) != NULL) {
// @encode(EXPBasicBlock) returns @? as of clang 4.1.
// This condition must occur before the test for id/class type,
// otherwise blocks will be treated as vanilla objects.
id actual = va_arg(v, EXPBasicBlock);
obj = [[actual copy] autorelease];
} else if((strstr(type, @encode(id)) != NULL) || (strstr(type, @encode(Class)) != 0)) {
id actual = va_arg(v, id);
obj = actual;
} else if(strcmp(type, @encode(__typeof__(nil))) == 0) {
obj = nil;
} else if(strstr(type, "ff}{") != NULL) { //TODO: of course this only works for a 2x2 e.g. CGRect
obj = [[[EXPFloatTuple alloc] initWithFloatValues:(float *)va_arg(v, float[4]) size:4] autorelease];
} else if(strstr(type, "=ff}") != NULL) {
obj = [[[EXPFloatTuple alloc] initWithFloatValues:(float *)va_arg(v, float[2]) size:2] autorelease];
} else if(strstr(type, "=ffff}") != NULL) {
obj = [[[EXPFloatTuple alloc] initWithFloatValues:(float *)va_arg(v, float[4]) size:4] autorelease];
} else if(strstr(type, "dd}{") != NULL) { //TODO: same here
obj = [[[EXPDoubleTuple alloc] initWithDoubleValues:(double *)va_arg(v, double[4]) size:4] autorelease];
} else if(strstr(type, "=dd}") != NULL) {
obj = [[[EXPDoubleTuple alloc] initWithDoubleValues:(double *)va_arg(v, double[2]) size:2] autorelease];
} else if(strstr(type, "=dddd}") != NULL) {
obj = [[[EXPDoubleTuple alloc] initWithDoubleValues:(double *)va_arg(v, double[4]) size:4] autorelease];
} else if(type[0] == '{') {
EXPUnsupportedObject *actual = [[[EXPUnsupportedObject alloc] initWithType:@"struct"] autorelease];
obj = actual;
} else if(type[0] == '(') {
EXPUnsupportedObject *actual = [[[EXPUnsupportedObject alloc] initWithType:@"union"] autorelease];
obj = actual;
} else {
void *actual = va_arg(v, void *);
obj = (actual == NULL ? nil :[NSValue valueWithPointer:actual]);
}
if([obj isKindOfClass:[NSValue class]] && ![obj isKindOfClass:[NSNumber class]]) {
[(NSValue *)obj set_EXP_objCType:type];
}
va_end(v);
return obj;
}
EXPExpect *_EXP_expect(id testCase, int lineNumber, const char *fileName, EXPIdBlock actualBlock) {
return [EXPExpect expectWithActualBlock:actualBlock testCase:testCase lineNumber:lineNumber fileName:fileName];
}
void EXPFail(id testCase, int lineNumber, const char *fileName, NSString *message) {
NSLog(@"%s:%d %@", fileName, lineNumber, message);
NSString *reason = [NSString stringWithFormat:@"%s:%d %@", fileName, lineNumber, message];
NSException *exception = [NSException exceptionWithName:@"Expecta Error" reason:reason userInfo:nil];
if(testCase && [testCase respondsToSelector:@selector(recordFailureWithDescription:inFile:atLine:expected:)]){
[testCase recordFailureWithDescription:message
inFile:@(fileName)
atLine:lineNumber
expected:NO];
} else {
[exception raise];
}
}
NSString *EXPDescribeObject(id obj) {
if(obj == nil) {
return @"nil/null";
} else if([obj isKindOfClass:[NSValue class]] && ![obj isKindOfClass:[NSNumber class]]) {
const char *type = [(NSValue *)obj _EXP_objCType];
if(type) {
if(strcmp(type, @encode(SEL)) == 0) {
return [NSString stringWithFormat:@"@selector(%@)", NSStringFromSelector([obj pointerValue])];
} else if(strcmp(type, @encode(Class)) == 0) {
return NSStringFromClass([obj pointerValue]);
}
}
}
NSString *description = [obj description];
if([obj isKindOfClass:[NSArray class]]) {
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:[obj count]];
for(id o in obj) {
[arr addObject:EXPDescribeObject(o)];
}
description = [NSString stringWithFormat:@"(%@)", [arr componentsJoinedByString:@", "]];
} else if([obj isKindOfClass:[NSSet class]] || [obj isKindOfClass:[NSOrderedSet class]]) {
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:[obj count]];
for(id o in obj) {
[arr addObject:EXPDescribeObject(o)];
}
description = [NSString stringWithFormat:@"{(%@)}", [arr componentsJoinedByString:@", "]];
} else if([obj isKindOfClass:[NSDictionary class]]) {
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:[obj count]];
for(id k in obj) {
id v = obj[k];
[arr addObject:[NSString stringWithFormat:@"%@ = %@;",EXPDescribeObject(k), EXPDescribeObject(v)]];
}
description = [NSString stringWithFormat:@"{%@}", [arr componentsJoinedByString:@" "]];
} else if([obj isKindOfClass:[NSAttributedString class]]) {
description = [obj string];
} else {
description = [description stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
}
return description;
}
void EXP_prerequisite(EXPBoolBlock block) {
[[[NSThread currentThread] threadDictionary][@"EXP_currentMatcher"] setPrerequisiteBlock:block];
}
void EXP_match(EXPBoolBlock block) {
[[[NSThread currentThread] threadDictionary][@"EXP_currentMatcher"] setMatchBlock:block];
}
void EXP_failureMessageForTo(EXPStringBlock block) {
[[[NSThread currentThread] threadDictionary][@"EXP_currentMatcher"] setFailureMessageForToBlock:block];
}
void EXP_failureMessageForNotTo(EXPStringBlock block) {
[[[NSThread currentThread] threadDictionary][@"EXP_currentMatcher"] setFailureMessageForNotToBlock:block];
}
@@ -0,0 +1,4 @@
#import <Foundation/Foundation.h>
BOOL EXPIsValuePointer(NSValue *value);
BOOL EXPIsNumberFloat(NSNumber *number);
@@ -0,0 +1,9 @@
#import "EXPMatcherHelpers.h"
BOOL EXPIsValuePointer(NSValue *value) {
return [value objCType][0] == @encode(void *)[0];
}
BOOL EXPIsNumberFloat(NSNumber *number) {
return strcmp([number objCType], @encode(float)) == 0;
}
@@ -0,0 +1,7 @@
#import "Expecta.h"
EXPMatcherInterface(_beCloseToWithin, (id expected, id within));
EXPMatcherInterface(beCloseToWithin, (id expected, id within));
#define beCloseTo(expected) _beCloseToWithin(EXPObjectify((expected)), nil)
#define beCloseToWithin(expected, range) _beCloseToWithin(EXPObjectify((expected)), EXPObjectify((range)))
@@ -0,0 +1,49 @@
#import "EXPMatchers+beCloseTo.h"
#import "EXPMatcherHelpers.h"
EXPMatcherImplementationBegin(_beCloseToWithin, (id expected, id within)) {
prerequisite(^BOOL{
return [actual isKindOfClass:[NSNumber class]] &&
[expected isKindOfClass:[NSNumber class]] &&
([within isKindOfClass:[NSNumber class]] || (within == nil));
});
match(^BOOL{
double actualValue = [actual doubleValue];
double expectedValue = [expected doubleValue];
if (within != nil) {
double withinValue = [within doubleValue];
double lowerBound = expectedValue - withinValue;
double upperBound = expectedValue + withinValue;
return (actualValue >= lowerBound) && (actualValue <= upperBound);
} else {
double diff = fabs(actualValue - expectedValue);
actualValue = fabs(actualValue);
expectedValue = fabs(expectedValue);
double largest = (expectedValue > actualValue) ? expectedValue : actualValue;
return (diff <= largest * FLT_EPSILON);
}
});
failureMessageForTo(^NSString *{
if (within) {
return [NSString stringWithFormat:@"expected %@ to be close to %@ within %@",
EXPDescribeObject(actual), EXPDescribeObject(expected), EXPDescribeObject(within)];
} else {
return [NSString stringWithFormat:@"expected %@ to be close to %@",
EXPDescribeObject(actual), EXPDescribeObject(expected)];
}
});
failureMessageForNotTo(^NSString *{
if (within) {
return [NSString stringWithFormat:@"expected %@ not to be close to %@ within %@",
EXPDescribeObject(actual), EXPDescribeObject(expected), EXPDescribeObject(within)];
} else {
return [NSString stringWithFormat:@"expected %@ not to be close to %@",
EXPDescribeObject(actual), EXPDescribeObject(expected)];
}
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,3 @@
#import "Expecta.h"
EXPMatcherInterface(beFalsy, (void));
@@ -0,0 +1,24 @@
#import "EXPMatchers+beFalsy.h"
#import "EXPMatcherHelpers.h"
EXPMatcherImplementationBegin(beFalsy, (void)) {
match(^BOOL{
if([actual isKindOfClass:[NSNumber class]]) {
return ![(NSNumber *)actual boolValue];
} else if([actual isKindOfClass:[NSValue class]]) {
if(EXPIsValuePointer((NSValue *)actual)) {
return ![(NSValue *)actual pointerValue];
}
}
return !actual;
});
failureMessageForTo(^NSString *{
return [NSString stringWithFormat:@"expected: a falsy value, got: %@, which is truthy", EXPDescribeObject(actual)];
});
failureMessageForNotTo(^NSString *{
return [NSString stringWithFormat:@"expected: a non-falsy value, got: %@, which is falsy", EXPDescribeObject(actual)];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,6 @@
#import "Expecta.h"
EXPMatcherInterface(_beGreaterThan, (id expected));
EXPMatcherInterface(beGreaterThan, (id expected));
#define beGreaterThan(expected) _beGreaterThan(EXPObjectify((expected)))
@@ -0,0 +1,20 @@
#import "EXPMatchers+beGreaterThan.h"
#import "EXPMatcherHelpers.h"
EXPMatcherImplementationBegin(_beGreaterThan, (id expected)) {
match(^BOOL{
if ([actual respondsToSelector:@selector(compare:)]) {
return [actual compare:expected] == NSOrderedDescending;
}
return NO;
});
failureMessageForTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@ to be greater than %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
failureMessageForNotTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@ not to be greater than %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,6 @@
#import "Expecta.h"
EXPMatcherInterface(_beGreaterThanOrEqualTo, (id expected));
EXPMatcherInterface(beGreaterThanOrEqualTo, (id expected));
#define beGreaterThanOrEqualTo(expected) _beGreaterThanOrEqualTo(EXPObjectify((expected)))
@@ -0,0 +1,20 @@
#import "EXPMatchers+beGreaterThanOrEqualTo.h"
#import "EXPMatcherHelpers.h"
EXPMatcherImplementationBegin(_beGreaterThanOrEqualTo, (id expected)) {
match(^BOOL{
if ([actual respondsToSelector:@selector(compare:)]) {
return [actual compare:expected] != NSOrderedAscending;
}
return NO;
});
failureMessageForTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@ to be greater than or equal to %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
failureMessageForNotTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@ not to be greater than or equal to %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,10 @@
#import "Expecta.h"
EXPMatcherInterface(_beIdenticalTo, (void *expected));
EXPMatcherInterface(beIdenticalTo, (void *expected)); // to aid code completion
#if __has_feature(objc_arc)
#define beIdenticalTo(expected) _beIdenticalTo((__bridge void*)expected)
#else
#define beIdenticalTo(expected) _beIdenticalTo(expected)
#endif
@@ -0,0 +1,24 @@
#import "EXPMatchers+equal.h"
#import "EXPMatcherHelpers.h"
EXPMatcherImplementationBegin(_beIdenticalTo, (void *expected)) {
match(^BOOL{
if(actual == expected) {
return YES;
} else if([actual isKindOfClass:[NSValue class]] && EXPIsValuePointer((NSValue *)actual)) {
if([(NSValue *)actual pointerValue] == expected) {
return YES;
}
}
return NO;
});
failureMessageForTo(^NSString *{
return [NSString stringWithFormat:@"expected: <%p>, got: <%p>", expected, actual];
});
failureMessageForNotTo(^NSString *{
return [NSString stringWithFormat:@"expected: not <%p>, got: <%p>", expected, actual];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,6 @@
#import "Expecta.h"
EXPMatcherInterface(_beInTheRangeOf, (id expectedLowerBound, id expectedUpperBound));
EXPMatcherInterface(beInTheRangeOf, (id expectedLowerBound, id expectedUpperBound));
#define beInTheRangeOf(expectedLowerBound, expectedUpperBound) _beInTheRangeOf(EXPObjectify((expectedLowerBound)), EXPObjectify((expectedUpperBound)))
@@ -0,0 +1,30 @@
#import "EXPMatchers+beInTheRangeOf.h"
#import "EXPMatcherHelpers.h"
EXPMatcherImplementationBegin(_beInTheRangeOf, (id expectedLowerBound, id expectedUpperBound)) {
match(^BOOL{
if ([actual respondsToSelector:@selector(compare:)]) {
NSComparisonResult compareLowerBound = [expectedLowerBound compare: actual];
NSComparisonResult compareUpperBound = [expectedUpperBound compare: actual];
if (compareLowerBound == NSOrderedSame) {
return YES;
}
if (compareUpperBound == NSOrderedSame) {
return YES;
}
if ((compareLowerBound == NSOrderedAscending) && (compareUpperBound == NSOrderedDescending)) {
return YES;
}
}
return NO;
});
failureMessageForTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@ to be in the range [%@, %@] (inclusive)", EXPDescribeObject(actual), EXPDescribeObject(expectedLowerBound), EXPDescribeObject(expectedUpperBound)];
});
failureMessageForNotTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@ not to be in the range [%@, %@] (inclusive)", EXPDescribeObject(actual), EXPDescribeObject(expectedLowerBound), EXPDescribeObject(expectedUpperBound)];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,6 @@
#import "Expecta.h"
EXPMatcherInterface(beInstanceOf, (Class expected));
EXPMatcherInterface(beAnInstanceOf, (Class expected));
EXPMatcherInterface(beMemberOf, (Class expected));
EXPMatcherInterface(beAMemberOf, (Class expected));
@@ -0,0 +1,31 @@
#import "EXPMatchers+beInstanceOf.h"
EXPMatcherImplementationBegin(beInstanceOf, (Class expected)) {
BOOL actualIsNil = (actual == nil);
BOOL expectedIsNil = (expected == nil);
prerequisite(^BOOL{
return !(actualIsNil || expectedIsNil);
});
match(^BOOL{
return [actual isMemberOfClass:expected];
});
failureMessageForTo(^NSString *{
if(actualIsNil) return @"the actual value is nil/null";
if(expectedIsNil) return @"the expected value is nil/null";
return [NSString stringWithFormat:@"expected: an instance of %@, got: an instance of %@", [expected class], [actual class]];
});
failureMessageForNotTo(^NSString *{
if(actualIsNil) return @"the actual value is nil/null";
if(expectedIsNil) return @"the expected value is nil/null";
return [NSString stringWithFormat:@"expected: not an instance of %@, got: an instance of %@", [expected class], [actual class]];
});
}
EXPMatcherImplementationEnd
EXPMatcherAliasImplementation(beAnInstanceOf, beInstanceOf, (Class expected));
EXPMatcherAliasImplementation(beMemberOf, beInstanceOf, (Class expected));
EXPMatcherAliasImplementation(beAMemberOf, beInstanceOf, (Class expected));
@@ -0,0 +1,4 @@
#import "Expecta.h"
EXPMatcherInterface(beKindOf, (Class expected));
EXPMatcherInterface(beAKindOf, (Class expected));
@@ -0,0 +1,29 @@
#import "EXPMatchers+beKindOf.h"
EXPMatcherImplementationBegin(beKindOf, (Class expected)) {
BOOL actualIsNil = (actual == nil);
BOOL expectedIsNil = (expected == nil);
prerequisite(^BOOL{
return !(actualIsNil || expectedIsNil);
});
match(^BOOL{
return [actual isKindOfClass:expected];
});
failureMessageForTo(^NSString *{
if(actualIsNil) return @"the actual value is nil/null";
if(expectedIsNil) return @"the expected value is nil/null";
return [NSString stringWithFormat:@"expected: a kind of %@, got: an instance of %@, which is not a kind of %@", [expected class], [actual class], [expected class]];
});
failureMessageForNotTo(^NSString *{
if(actualIsNil) return @"the actual value is nil/null";
if(expectedIsNil) return @"the expected value is nil/null";
return [NSString stringWithFormat:@"expected: not a kind of %@, got: an instance of %@, which is a kind of %@", [expected class], [actual class], [expected class]];
});
}
EXPMatcherImplementationEnd
EXPMatcherAliasImplementation(beAKindOf, beKindOf, (Class expected));
@@ -0,0 +1,6 @@
#import "Expecta.h"
EXPMatcherInterface(_beLessThan, (id expected));
EXPMatcherInterface(beLessThan, (id expected));
#define beLessThan(expected) _beLessThan(EXPObjectify((expected)))
@@ -0,0 +1,20 @@
#import "EXPMatchers+beLessThan.h"
#import "EXPMatcherHelpers.h"
EXPMatcherImplementationBegin(_beLessThan, (id expected)) {
match(^BOOL{
if ([actual respondsToSelector:@selector(compare:)]) {
return [actual compare:expected] == NSOrderedAscending;
}
return NO;
});
failureMessageForTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@ to be less than %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
failureMessageForNotTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@ not to be less than %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,6 @@
#import "Expecta.h"
EXPMatcherInterface(_beLessThanOrEqualTo, (id expected));
EXPMatcherInterface(beLessThanOrEqualTo, (id expected));
#define beLessThanOrEqualTo(expected) _beLessThanOrEqualTo(EXPObjectify((expected)))
@@ -0,0 +1,20 @@
#import "EXPMatchers+beLessThanOrEqualTo.h"
#import "EXPMatcherHelpers.h"
EXPMatcherImplementationBegin(_beLessThanOrEqualTo, (id expected)) {
match(^BOOL{
if ([actual respondsToSelector:@selector(compare:)]) {
return [actual compare:expected] != NSOrderedDescending;
}
return NO;
});
failureMessageForTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@ to be less than or equal to %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
failureMessageForNotTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@ not to be less than or equal to %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,4 @@
#import "Expecta.h"
EXPMatcherInterface(beNil, (void));
EXPMatcherInterface(beNull, (void));
@@ -0,0 +1,18 @@
#import "EXPMatchers+beNil.h"
EXPMatcherImplementationBegin(beNil, (void)) {
match(^BOOL{
return actual == nil;
});
failureMessageForTo(^NSString *{
return [NSString stringWithFormat:@"expected: nil/null, got: %@", EXPDescribeObject(actual)];
});
failureMessageForNotTo(^NSString *{
return [NSString stringWithFormat:@"expected: not nil/null, got: %@", EXPDescribeObject(actual)];
});
}
EXPMatcherImplementationEnd
EXPMatcherAliasImplementation(beNull, beNil, (void));
@@ -0,0 +1,4 @@
#import "Expecta.h"
EXPMatcherInterface(beSubclassOf, (Class expected));
EXPMatcherInterface(beASubclassOf, (Class expected));
@@ -0,0 +1,29 @@
#import "EXPMatchers+beSubclassOf.h"
#import "NSValue+Expecta.h"
#import <objc/runtime.h>
EXPMatcherImplementationBegin(beSubclassOf, (Class expected)) {
__block BOOL actualIsClass = YES;
prerequisite(^BOOL {
actualIsClass = class_isMetaClass(object_getClass(actual));
return actualIsClass;
});
match(^BOOL{
return [actual isSubclassOfClass:expected];
});
failureMessageForTo(^NSString *{
if(!actualIsClass) return @"the actual value is not a Class";
return [NSString stringWithFormat:@"expected: a subclass of %@, got: a class %@, which is not a subclass of %@", [expected class], actual, [expected class]];
});
failureMessageForNotTo(^NSString *{
if(!actualIsClass) return @"the actual value is not a Class";
return [NSString stringWithFormat:@"expected: not a subclass of %@, got: a class %@, which is a subclass of %@", [expected class], actual, [expected class]];
});
}
EXPMatcherImplementationEnd
EXPMatcherAliasImplementation(beASubclassOf, beSubclassOf, (Class expected));
@@ -0,0 +1,4 @@
#import "Expecta.h"
EXPMatcherInterface(beSupersetOf, (id subset));
@@ -0,0 +1,62 @@
#import "EXPMatchers+contain.h"
EXPMatcherImplementationBegin(beSupersetOf, (id subset)) {
BOOL actualIsCompatible = [actual isKindOfClass:[NSDictionary class]] || [actual respondsToSelector:@selector(containsObject:)];
BOOL subsetIsNil = (subset == nil);
// For some instances the isKindOfClass: method returns false, even though
// they are both actually dictionaries. e.g. Comparing a NSCFDictionary and a
// NSDictionary.
// Or in cases when you compare NSMutableArray (which implementation is __NSArrayM:NSMutableArray:NSArray)
// and NSArray (which implementation is __NSArrayI:NSArray)
BOOL bothAreIdenticalCollectionClasses = ([actual isKindOfClass:[NSDictionary class]] && [subset isKindOfClass:[NSDictionary class]]) ||
([actual isKindOfClass:[NSArray class]] && [subset isKindOfClass:[NSArray class]]) ||
([actual isKindOfClass:[NSSet class]] && [subset isKindOfClass:[NSSet class]]) ||
([actual isKindOfClass:[NSOrderedSet class]] && [subset isKindOfClass:[NSOrderedSet class]]);
BOOL classMatches = bothAreIdenticalCollectionClasses || [subset isKindOfClass:[actual class]];
prerequisite(^BOOL{
return actualIsCompatible && !subsetIsNil && classMatches;
});
match(^BOOL{
if(!actualIsCompatible) return NO;
if([actual isKindOfClass:[NSDictionary class]]) {
for (id key in subset) {
id actualValue = [actual valueForKey:key];
id subsetValue = [subset valueForKey:key];
if (![subsetValue isEqual:actualValue]) return NO;
}
} else {
for (id object in subset) {
if (![actual containsObject:object]) return NO;
}
}
return YES;
});
failureMessageForTo(^NSString *{
if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSDictionary and does not implement -containsObject:", EXPDescribeObject(actual)];
if(subsetIsNil) return @"the expected value is nil/null";
if(!classMatches) return [NSString stringWithFormat:@"%@ does not match the class of %@", EXPDescribeObject(subset), EXPDescribeObject(actual)];
return [NSString stringWithFormat:@"expected %@ to be a superset of %@", EXPDescribeObject(actual), EXPDescribeObject(subset)];
});
failureMessageForNotTo(^NSString *{
if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSDictionary and does not implement -containsObject:", EXPDescribeObject(actual)];
if(subsetIsNil) return @"the expected value is nil/null";
if(!classMatches) return [NSString stringWithFormat:@"%@ does not match the class of %@", EXPDescribeObject(subset), EXPDescribeObject(actual)];
return [NSString stringWithFormat:@"expected %@ not to be a superset of %@", EXPDescribeObject(actual), EXPDescribeObject(subset)];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,3 @@
#import "Expecta.h"
EXPMatcherInterface(beTruthy, (void));
@@ -0,0 +1,24 @@
#import "EXPMatchers+beTruthy.h"
#import "EXPMatcherHelpers.h"
EXPMatcherImplementationBegin(beTruthy, (void)) {
match(^BOOL{
if([actual isKindOfClass:[NSNumber class]]) {
return !![(NSNumber *)actual boolValue];
} else if([actual isKindOfClass:[NSValue class]]) {
if(EXPIsValuePointer((NSValue *)actual)) {
return !![(NSValue *)actual pointerValue];
}
}
return !!actual;
});
failureMessageForTo(^NSString *{
return [NSString stringWithFormat:@"expected: a truthy value, got: %@, which is falsy", EXPDescribeObject(actual)];
});
failureMessageForNotTo(^NSString *{
return [NSString stringWithFormat:@"expected: a non-truthy value, got: %@, which is truthy", EXPDescribeObject(actual)];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,4 @@
#import "Expecta.h"
EXPMatcherInterface(beginWith, (id expected));
EXPMatcherInterface(startWith, (id expected));
@@ -0,0 +1,51 @@
#import "EXPMatchers+beginWith.h"
EXPMatcherImplementationBegin(beginWith, (id expected)) {
BOOL actualIsNil = (actual == nil);
BOOL expectedIsNil = (expected == nil);
//This condition allows the comparison of an immutable string or ordered collection to the mutable type of the same
BOOL actualAndExpectedAreCompatible = (([actual isKindOfClass:[NSString class]] && [expected isKindOfClass:[NSString class]])
|| ([actual isKindOfClass:[NSArray class]] && [expected isKindOfClass:[NSArray class]])
|| ([actual isKindOfClass:[NSOrderedSet class]] && [expected isKindOfClass:[NSOrderedSet class]]));
prerequisite(^BOOL {
return actualAndExpectedAreCompatible;
});
match(^BOOL {
if ([actual isKindOfClass:[NSString class]]) {
return [actual hasPrefix:expected];
} else if ([actual isKindOfClass:[NSArray class]]) {
if ([expected count] > [actual count] || [expected count] == 0) {
return NO;
}
NSArray *subArray = [actual subarrayWithRange:NSMakeRange(0, [expected count])];
return [subArray isEqualToArray:expected];
} else {
if ([expected count] > [actual count] || [expected count] == 0) {
return NO;
}
NSOrderedSet *subset = [NSOrderedSet orderedSetWithOrderedSet:actual range:NSMakeRange(0, [expected count]) copyItems:NO];
return [subset isEqualToOrderedSet:expected];
}
});
failureMessageForTo(^NSString *{
if (actualIsNil) return @"the object is nil/null";
if (expectedIsNil) return @"the expected value is nil/null";
if (!actualAndExpectedAreCompatible) return [NSString stringWithFormat:@"%@ and %@ are not instances of one of %@, %@, or %@", EXPDescribeObject(actual), EXPDescribeObject(expected), [NSString class], [NSArray class], [NSOrderedSet class]];
return [NSString stringWithFormat:@"expected: %@ to begin with %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
failureMessageForNotTo(^NSString *{
if (actualIsNil) return @"the object is nil/null";
if (expectedIsNil) return @"the expected value is nil/null";
if (!actualAndExpectedAreCompatible) return [NSString stringWithFormat:@"%@ and %@ are not instances of one of %@, %@, or %@", EXPDescribeObject(actual), EXPDescribeObject(expected), [NSString class], [NSArray class], [NSOrderedSet class]];
return [NSString stringWithFormat:@"expected: %@ not to begin with %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
}
EXPMatcherImplementationEnd
EXPMatcherAliasImplementation(startWith, beginWith, (id expected));
@@ -0,0 +1,3 @@
#import "Expecta.h"
EXPMatcherInterface(conformTo, (Protocol *expected));
@@ -0,0 +1,33 @@
#import "EXPMatchers+conformTo.h"
#import "NSValue+Expecta.h"
#import <objc/runtime.h>
EXPMatcherImplementationBegin(conformTo, (Protocol *expected)) {
BOOL actualIsNil = (actual == nil);
BOOL expectedIsNil = (expected == nil);
prerequisite(^BOOL{
return !(actualIsNil || expectedIsNil);
});
match(^BOOL{
return [actual conformsToProtocol:expected];
});
failureMessageForTo(^NSString *{
if(actualIsNil) return @"the object is nil/null";
if(expectedIsNil) return @"the protocol is nil/null";
NSString *name = NSStringFromProtocol(expected);
return [NSString stringWithFormat:@"expected: %@ to conform to %@", actual, name];
});
failureMessageForNotTo(^NSString *{
if(actualIsNil) return @"the object is nil/null";
if(expectedIsNil) return @"the protocol is nil/null";
NSString *name = NSStringFromProtocol(expected);
return [NSString stringWithFormat:@"expected: %@ not to conform to %@", actual, name];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,5 @@
#import "Expecta.h"
EXPMatcherInterface(_contain, (id expected));
EXPMatcherInterface(contain, (id expected)); // to aid code completion
#define contain(expected) _contain(EXPObjectify((expected)))
@@ -0,0 +1,38 @@
#import "EXPMatchers+contain.h"
EXPMatcherImplementationBegin(_contain, (id expected)) {
BOOL actualIsCompatible = [actual isKindOfClass:[NSString class]] || [actual conformsToProtocol:@protocol(NSFastEnumeration)];
BOOL expectedIsNil = (expected == nil);
prerequisite(^BOOL{
return actualIsCompatible && !expectedIsNil;
});
match(^BOOL{
if(actualIsCompatible) {
if([actual isKindOfClass:[NSString class]]) {
return [(NSString *)actual rangeOfString:[expected description]].location != NSNotFound;
} else {
for (id object in actual) {
if ([object isEqual:expected]) {
return YES;
}
}
}
}
return NO;
});
failureMessageForTo(^NSString *{
if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSString or NSFastEnumeration", EXPDescribeObject(actual)];
if(expectedIsNil) return @"the expected value is nil/null";
return [NSString stringWithFormat:@"expected %@ to contain %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
failureMessageForNotTo(^NSString *{
if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSString or NSFastEnumeration", EXPDescribeObject(actual)];
if(expectedIsNil) return @"the expected value is nil/null";
return [NSString stringWithFormat:@"expected %@ not to contain %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,3 @@
#import "Expecta.h"
EXPMatcherInterface(endWith, (id expected));
@@ -0,0 +1,49 @@
#import "EXPMatchers+endWith.h"
EXPMatcherImplementationBegin(endWith, (id expected)) {
BOOL actualIsNil = (actual == nil);
BOOL expectedIsNil = (expected == nil);
//This condition allows the comparison of an immutable string or ordered collection to the mutable type of the same
BOOL actualAndExpectedAreCompatible = (([actual isKindOfClass:[NSString class]] && [expected isKindOfClass:[NSString class]])
|| ([actual isKindOfClass:[NSArray class]] && [expected isKindOfClass:[NSArray class]])
|| ([actual isKindOfClass:[NSOrderedSet class]] && [expected isKindOfClass:[NSOrderedSet class]]));
prerequisite(^BOOL {
return actualAndExpectedAreCompatible;
});
match(^BOOL {
if ([actual isKindOfClass:[NSString class]]) {
return [actual hasSuffix:expected];
} else if ([actual isKindOfClass:[NSArray class]]) {
if ([expected count] > [actual count] || [expected count] == 0) {
return NO;
}
NSArray *subArray = [actual subarrayWithRange:NSMakeRange([actual count] - [expected count], [expected count])];
return [subArray isEqualToArray:expected];
} else {
if ([expected count] > [actual count] || [expected count] == 0) {
return NO;
}
NSOrderedSet *subset = [NSOrderedSet orderedSetWithOrderedSet:actual range:NSMakeRange([actual count] - [expected count], [expected count]) copyItems:NO];
return [subset isEqualToOrderedSet:expected];
}
});
failureMessageForTo(^NSString *{
if (actualIsNil) return @"the object is nil/null";
if (expectedIsNil) return @"the expected value is nil/null";
if (!actualAndExpectedAreCompatible) return [NSString stringWithFormat:@"%@ and %@ are not instances of one of %@, %@, or %@", EXPDescribeObject(actual), EXPDescribeObject(expected), [NSString class], [NSArray class], [NSOrderedSet class]];
return [NSString stringWithFormat:@"expected: %@ to end with %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
failureMessageForNotTo(^NSString *{
if (actualIsNil) return @"the object is nil/null";
if (expectedIsNil) return @"the expected value is nil/null";
if (!actualAndExpectedAreCompatible) return [NSString stringWithFormat:@"%@ and %@ are not instances of one of %@, %@, or %@", EXPDescribeObject(actual), EXPDescribeObject(expected), [NSString class], [NSArray class], [NSOrderedSet class]];
return [NSString stringWithFormat:@"expected: %@ not to end with %@", EXPDescribeObject(actual), EXPDescribeObject(expected)];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,5 @@
#import "Expecta.h"
EXPMatcherInterface(_equal, (id expected));
EXPMatcherInterface(equal, (id expected)); // to aid code completion
#define equal(...) _equal(EXPObjectify((__VA_ARGS__)))
@@ -0,0 +1,38 @@
#import "EXPMatchers+equal.h"
#import "EXPMatcherHelpers.h"
EXPMatcherImplementationBegin(_equal, (id expected)) {
match(^BOOL{
if((actual == expected) || [actual isEqual:expected]) {
return YES;
} else if([actual isKindOfClass:[NSNumber class]] && [expected isKindOfClass:[NSNumber class]]) {
if([actual isKindOfClass:[NSDecimalNumber class]] || [expected isKindOfClass:[NSDecimalNumber class]]) {
NSDecimalNumber *actualDecimalNumber = [NSDecimalNumber decimalNumberWithDecimal:[(NSNumber *) actual decimalValue]];
NSDecimalNumber *expectedDecimalNumber = [NSDecimalNumber decimalNumberWithDecimal:[(NSNumber *) expected decimalValue]];
return [actualDecimalNumber isEqualToNumber:expectedDecimalNumber];
}
else {
if(EXPIsNumberFloat((NSNumber *)actual) || EXPIsNumberFloat((NSNumber *)expected)) {
return [(NSNumber *)actual floatValue] == [(NSNumber *)expected floatValue];
}
}
}
return NO;
});
failureMessageForTo(^NSString *{
NSString *expectedDescription = EXPDescribeObject(expected);
NSString *actualDescription = EXPDescribeObject(actual);
if (![expectedDescription isEqualToString:actualDescription]) {
return [NSString stringWithFormat:@"expected: %@, got: %@", EXPDescribeObject(expected), EXPDescribeObject(actual)];
} else {
return [NSString stringWithFormat:@"expected (%@): %@, got (%@): %@", NSStringFromClass([expected class]), EXPDescribeObject(expected), NSStringFromClass([actual class]), EXPDescribeObject(actual)];
}
});
failureMessageForNotTo(^NSString *{
return [NSString stringWithFormat:@"expected: not %@, got: %@", EXPDescribeObject(expected), EXPDescribeObject(actual)];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,10 @@
#import "Expecta.h"
EXPMatcherInterface(haveCountOf, (NSUInteger expected));
EXPMatcherInterface(haveCount, (NSUInteger expected));
EXPMatcherInterface(haveACountOf, (NSUInteger expected));
EXPMatcherInterface(haveLength, (NSUInteger expected));
EXPMatcherInterface(haveLengthOf, (NSUInteger expected));
EXPMatcherInterface(haveALengthOf, (NSUInteger expected));
#define beEmpty() haveCountOf(0)
@@ -0,0 +1,42 @@
#import "EXPMatchers+haveCountOf.h"
EXPMatcherImplementationBegin(haveCountOf, (NSUInteger expected)) {
BOOL actualIsStringy = [actual isKindOfClass:[NSString class]] || [actual isKindOfClass:[NSAttributedString class]];
BOOL actualIsCompatible = actualIsStringy || [actual respondsToSelector:@selector(count)];
prerequisite(^BOOL{
return actualIsCompatible;
});
NSUInteger (^count)(id) = ^(id actual) {
if(actualIsStringy) {
return [actual length];
} else {
return [actual count];
}
};
match(^BOOL{
if(actualIsCompatible) {
return count(actual) == expected;
}
return NO;
});
failureMessageForTo(^NSString *{
if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSString, NSAttributedString, NSArray, NSSet, NSOrderedSet, or NSDictionary", EXPDescribeObject(actual)];
return [NSString stringWithFormat:@"expected %@ to have a count of %zi but got %zi", EXPDescribeObject(actual), expected, count(actual)];
});
failureMessageForNotTo(^NSString *{
if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSString, NSAttributedString, NSArray, NSSet, NSOrderedSet, or NSDictionary", EXPDescribeObject(actual)];
return [NSString stringWithFormat:@"expected %@ not to have a count of %zi", EXPDescribeObject(actual), expected];
});
}
EXPMatcherImplementationEnd
EXPMatcherAliasImplementation(haveCount, haveCountOf, (NSUInteger expected));
EXPMatcherAliasImplementation(haveACountOf, haveCountOf, (NSUInteger expected));
EXPMatcherAliasImplementation(haveLength, haveCountOf, (NSUInteger expected));
EXPMatcherAliasImplementation(haveLengthOf, haveCountOf, (NSUInteger expected));
EXPMatcherAliasImplementation(haveALengthOf, haveCountOf, (NSUInteger expected));
@@ -0,0 +1,3 @@
#import "Expecta.h"
EXPMatcherInterface(match, (NSString *expected));
@@ -0,0 +1,38 @@
#import "EXPMatchers+match.h"
#import "EXPMatcherHelpers.h"
EXPMatcherImplementationBegin(match, (NSString *expected)) {
BOOL actualIsNil = (actual == nil);
BOOL expectedIsNil = (expected == nil);
__block NSRegularExpression *regex = nil;
__block NSError *regexError = nil;
prerequisite (^BOOL {
BOOL nilInput = (actualIsNil || expectedIsNil);
if (!nilInput) {
regex = [NSRegularExpression regularExpressionWithPattern:expected options:0 error:&regexError];
}
return !nilInput && regex;
});
match(^BOOL {
NSRange range = [regex rangeOfFirstMatchInString:actual options:0 range:NSMakeRange(0, [actual length])];
return !NSEqualRanges(range, NSMakeRange(NSNotFound, 0));
});
failureMessageForTo(^NSString *{
if (actualIsNil) return @"the object is nil/null";
if (expectedIsNil) return @"the expression is nil/null";
if (regexError) return [NSString stringWithFormat:@"unable to create regular expression from given parameter: %@", [regexError localizedDescription]];
return [NSString stringWithFormat:@"expected: %@ to match to %@", EXPDescribeObject(actual), expected];
});
failureMessageForNotTo(^NSString *{
if (actualIsNil) return @"the object is nil/null";
if (expectedIsNil) return @"the expression is nil/null";
if (regexError) return [NSString stringWithFormat:@"unable to create regular expression from given parameter: %@", [regexError localizedDescription]];
return [NSString stringWithFormat:@"expected: %@ not to match to %@", EXPDescribeObject(actual), expected];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,4 @@
#import "Expecta.h"
EXPMatcherInterface(postNotification, (id expectedNotification));
EXPMatcherInterface(notify, (id expectedNotification));
@@ -0,0 +1,88 @@
#import "EXPMatchers+postNotification.h"
@implementation NSNotification (EXPEquality)
- (BOOL)exp_isFunctionallyEqualTo:(NSNotification *)otherNotification
{
if (![otherNotification isKindOfClass:[NSNotification class]]) return NO;
BOOL namesMatch = [otherNotification.name isEqualToString:self.name];
BOOL objectsMatch = YES;
if (otherNotification.object || self.object) {
objectsMatch = [otherNotification.object isEqual:self.object];
}
BOOL userInfoMatches = YES;
if (otherNotification.userInfo || self.userInfo) {
userInfoMatches = [otherNotification.userInfo isEqual:self.userInfo];
}
return (namesMatch && objectsMatch && userInfoMatches);
}
@end
EXPMatcherImplementationBegin(postNotification, (id expected)){
BOOL actualIsNil = (actual == nil);
BOOL expectedIsNil = (expected == nil);
BOOL isNotification = [expected isKindOfClass:[NSNotification class]];
BOOL isName = [expected isKindOfClass:[NSString class]];
__block NSString *expectedName;
__block BOOL expectedNotificationOccurred = NO;
__block id observer;
prerequisite(^BOOL{
expectedNotificationOccurred = NO;
if (actualIsNil || expectedIsNil) return NO;
if (isNotification) {
expectedName = [expected name];
}else if(isName) {
expectedName = expected;
}else{
return NO;
}
observer = [[NSNotificationCenter defaultCenter] addObserverForName:expectedName object:nil queue:nil usingBlock:^(NSNotification *note){
if (isNotification) {
expectedNotificationOccurred |= [expected exp_isFunctionallyEqualTo:note];
}else{
expectedNotificationOccurred = YES;
}
}];
((EXPBasicBlock)actual)();
return YES;
});
match(^BOOL{
if(expectedNotificationOccurred) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}
return expectedNotificationOccurred;
});
failureMessageForTo(^NSString *{
if (observer) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}
if(actualIsNil) return @"the actual value is nil/null";
if(expectedIsNil) return @"the expected value is nil/null";
if(!(isNotification || isName)) return @"the actual value is not a notification or string";
return [NSString stringWithFormat:@"expected: %@, got: none",expectedName];
});
failureMessageForNotTo(^NSString *{
if (observer) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}
if(actualIsNil) return @"the actual value is nil/null";
if(expectedIsNil) return @"the expected value is nil/null";
if(!(isNotification || isName)) return @"the actual value is not a notification or string";
return [NSString stringWithFormat:@"expected: none, got: %@", expectedName];
});
}
EXPMatcherImplementationEnd
EXPMatcherAliasImplementation(notify, postNotification, (id expectedNotification))
@@ -0,0 +1,4 @@
#import "Expecta.h"
EXPMatcherInterface(raise, (NSString *expectedExceptionName));
#define raiseAny() raise(nil)
@@ -0,0 +1,30 @@
#import "EXPMatchers+raise.h"
#import "EXPDefines.h"
EXPMatcherImplementationBegin(raise, (NSString *expectedExceptionName)) {
__block NSException *exceptionCaught = nil;
match(^BOOL{
BOOL expectedExceptionCaught = NO;
@try {
((EXPBasicBlock)actual)();
} @catch(NSException *e) {
exceptionCaught = e;
expectedExceptionCaught = (expectedExceptionName == nil) || [[exceptionCaught name] isEqualToString:expectedExceptionName];
}
return expectedExceptionCaught;
});
failureMessageForTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@, got: %@",
expectedExceptionName ? expectedExceptionName : @"any exception",
exceptionCaught ? [exceptionCaught name] : @"no exception"];
});
failureMessageForNotTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@, got: %@",
expectedExceptionName ? [NSString stringWithFormat:@"not %@", expectedExceptionName] : @"no exception",
exceptionCaught ? [exceptionCaught name] : @"no exception"];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,3 @@
#import "Expecta.h"
EXPMatcherInterface(raiseWithReason, (NSString *expectedExceptionName, NSString *expectedReason));
@@ -0,0 +1,35 @@
#import "EXPMatchers+raiseWithReason.h"
#import "EXPDefines.h"
EXPMatcherImplementationBegin(raiseWithReason, (NSString *expectedExceptionName, NSString *expectedReason)) {
__block NSException *exceptionCaught = nil;
match(^BOOL{
BOOL expectedExceptionCaught = NO;
@try {
((EXPBasicBlock)actual)();
} @catch(NSException *e) {
exceptionCaught = e;
expectedExceptionCaught = (((expectedExceptionName == nil) || [[exceptionCaught name] isEqualToString:expectedExceptionName]) &&
((expectedReason == nil) || ([[exceptionCaught reason] isEqualToString:expectedReason])));
}
return expectedExceptionCaught;
});
failureMessageForTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@ (%@), got: %@ (%@)",
expectedExceptionName ?: @"any exception",
expectedReason ?: @"any reason",
exceptionCaught ? [exceptionCaught name] : @"no exception",
exceptionCaught ? [exceptionCaught reason] : @""];
});
failureMessageForNotTo(^NSString *{
return [NSString stringWithFormat:@"expected: %@ (%@), got: %@ (%@)",
expectedExceptionName ? [NSString stringWithFormat:@"not %@", expectedExceptionName] : @"no exception",
expectedReason ? [NSString stringWithFormat:@"not '%@'", expectedReason] : @"no reason",
exceptionCaught ? [exceptionCaught name] : @"no exception",
exceptionCaught ? [exceptionCaught reason] : @"no reason"];
});
}
EXPMatcherImplementationEnd
@@ -0,0 +1,3 @@
#import "Expecta.h"
EXPMatcherInterface(respondTo, (SEL expected));
@@ -0,0 +1,28 @@
#import "EXPMatchers+respondTo.h"
#import "EXPMatcherHelpers.h"
EXPMatcherImplementationBegin(respondTo, (SEL expected)) {
BOOL actualIsNil = (actual == nil);
BOOL expectedIsNull = (expected == NULL);
prerequisite (^BOOL {
return !(actualIsNil || expectedIsNull);
});
match(^BOOL {
return [actual respondsToSelector:expected];
});
failureMessageForTo(^NSString *{
if (actualIsNil) return @"the object is nil/null";
if (expectedIsNull) return @"the selector is null";
return [NSString stringWithFormat:@"expected: %@ to respond to %@", EXPDescribeObject(actual), NSStringFromSelector(expected)];
});
failureMessageForNotTo(^NSString *{
if (actualIsNil) return @"the object is nil/null";
if (expectedIsNull) return @"the selector is null";
return [NSString stringWithFormat:@"expected: %@ not to respond to %@", EXPDescribeObject(actual), NSStringFromSelector(expected)];
});
}
EXPMatcherImplementationEnd
+25
View File
@@ -0,0 +1,25 @@
#import "EXPMatchers+beNil.h"
#import "EXPMatchers+equal.h"
#import "EXPMatchers+beInstanceOf.h"
#import "EXPMatchers+beKindOf.h"
#import "EXPMatchers+beSubclassOf.h"
#import "EXPMatchers+conformTo.h"
#import "EXPMatchers+beTruthy.h"
#import "EXPMatchers+beFalsy.h"
#import "EXPMatchers+contain.h"
#import "EXPMatchers+beSupersetOf.h"
#import "EXPMatchers+haveCountOf.h"
#import "EXPMatchers+beIdenticalTo.h"
#import "EXPMatchers+beGreaterThan.h"
#import "EXPMatchers+beGreaterThanOrEqualTo.h"
#import "EXPMatchers+beLessThan.h"
#import "EXPMatchers+beLessThanOrEqualTo.h"
#import "EXPMatchers+beInTheRangeOf.h"
#import "EXPMatchers+beCloseTo.h"
#import "EXPMatchers+raise.h"
#import "EXPMatchers+raiseWithReason.h"
#import "EXPMatchers+respondTo.h"
#import "EXPMatchers+postNotification.h"
#import "EXPMatchers+beginWith.h"
#import "EXPMatchers+endWith.h"
#import "EXPMatchers+match.h"
+10
View File
@@ -0,0 +1,10 @@
#import <Foundation/Foundation.h>
@interface NSObject (Expecta)
- (void)recordFailureWithDescription:(NSString *)description
inFile:(NSString *)filename
atLine:(NSUInteger)lineNumber
expected:(BOOL)expected;
@end
+7
View File
@@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>
@interface NSValue (Expecta)
@property (nonatomic) const char *_EXP_objCType;
@end
+21
View File
@@ -0,0 +1,21 @@
#import "NSValue+Expecta.h"
#import <objc/runtime.h>
#import "Expecta.h"
EXPFixCategoriesBug(NSValue_Expecta);
@implementation NSValue (Expecta)
static char _EXP_typeKey;
- (const char *)_EXP_objCType {
return [(NSString *)objc_getAssociatedObject(self, &_EXP_typeKey) cStringUsingEncoding:NSASCIIStringEncoding];
}
- (void)set_EXP_objCType:(const char *)_EXP_objCType {
objc_setAssociatedObject(self, &_EXP_typeKey,
@(_EXP_objCType),
OBJC_ASSOCIATION_COPY_NONATOMIC);
}
@end