/
ExpectaSupport.m
176 lines (163 loc) · 7.54 KB
/
ExpectaSupport.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#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];
}