OLD | NEW |
| (Empty) |
1 // | |
2 // GTMSenTestCase.m | |
3 // | |
4 // Copyright 2007-2008 Google Inc. | |
5 // | |
6 // Licensed under the Apache License, Version 2.0 (the "License"); you may not | |
7 // use this file except in compliance with the License. You may obtain a copy | |
8 // of the License at | |
9 // | |
10 // http://www.apache.org/licenses/LICENSE-2.0 | |
11 // | |
12 // Unless required by applicable law or agreed to in writing, software | |
13 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
14 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
15 // License for the specific language governing permissions and limitations unde
r | |
16 // the License. | |
17 // | |
18 | |
19 #import "GTMSenTestCase.h" | |
20 #import <unistd.h> | |
21 | |
22 #if !GTM_IPHONE_SDK | |
23 #import "GTMGarbageCollection.h" | |
24 #endif // !GTM_IPHONE_SDK | |
25 | |
26 #if GTM_IPHONE_SDK | |
27 #import <stdarg.h> | |
28 | |
29 @interface NSException (GTMSenTestPrivateAdditions) | |
30 + (NSException *)failureInFile:(NSString *)filename | |
31 atLine:(int)lineNumber | |
32 reason:(NSString *)reason; | |
33 @end | |
34 | |
35 @implementation NSException (GTMSenTestPrivateAdditions) | |
36 + (NSException *)failureInFile:(NSString *)filename | |
37 atLine:(int)lineNumber | |
38 reason:(NSString *)reason { | |
39 NSDictionary *userInfo = | |
40 [NSDictionary dictionaryWithObjectsAndKeys: | |
41 [NSNumber numberWithInteger:lineNumber], SenTestLineNumberKey, | |
42 filename, SenTestFilenameKey, | |
43 nil]; | |
44 | |
45 return [self exceptionWithName:SenTestFailureException | |
46 reason:reason | |
47 userInfo:userInfo]; | |
48 } | |
49 @end | |
50 | |
51 @implementation NSException (GTMSenTestAdditions) | |
52 | |
53 + (NSException *)failureInFile:(NSString *)filename | |
54 atLine:(int)lineNumber | |
55 withDescription:(NSString *)formatString, ... { | |
56 | |
57 NSString *testDescription = @""; | |
58 if (formatString) { | |
59 va_list vl; | |
60 va_start(vl, formatString); | |
61 testDescription = | |
62 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; | |
63 va_end(vl); | |
64 } | |
65 | |
66 NSString *reason = testDescription; | |
67 | |
68 return [self failureInFile:filename atLine:lineNumber reason:reason]; | |
69 } | |
70 | |
71 + (NSException *)failureInCondition:(NSString *)condition | |
72 isTrue:(BOOL)isTrue | |
73 inFile:(NSString *)filename | |
74 atLine:(int)lineNumber | |
75 withDescription:(NSString *)formatString, ... { | |
76 | |
77 NSString *testDescription = @""; | |
78 if (formatString) { | |
79 va_list vl; | |
80 va_start(vl, formatString); | |
81 testDescription = | |
82 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; | |
83 va_end(vl); | |
84 } | |
85 | |
86 NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@", | |
87 condition, isTrue ? "TRUE" : "FALSE", testDescription]; | |
88 | |
89 return [self failureInFile:filename atLine:lineNumber reason:reason]; | |
90 } | |
91 | |
92 + (NSException *)failureInEqualityBetweenObject:(id)left | |
93 andObject:(id)right | |
94 inFile:(NSString *)filename | |
95 atLine:(int)lineNumber | |
96 withDescription:(NSString *)formatString, ... { | |
97 | |
98 NSString *testDescription = @""; | |
99 if (formatString) { | |
100 va_list vl; | |
101 va_start(vl, formatString); | |
102 testDescription = | |
103 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; | |
104 va_end(vl); | |
105 } | |
106 | |
107 NSString *reason = | |
108 [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", | |
109 [left description], [right description], testDescription]; | |
110 | |
111 return [self failureInFile:filename atLine:lineNumber reason:reason]; | |
112 } | |
113 | |
114 + (NSException *)failureInEqualityBetweenValue:(NSValue *)left | |
115 andValue:(NSValue *)right | |
116 withAccuracy:(NSValue *)accuracy | |
117 inFile:(NSString *)filename | |
118 atLine:(int)lineNumber | |
119 withDescription:(NSString *)formatString, ... { | |
120 | |
121 NSString *testDescription = @""; | |
122 if (formatString) { | |
123 va_list vl; | |
124 va_start(vl, formatString); | |
125 testDescription = | |
126 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; | |
127 va_end(vl); | |
128 } | |
129 | |
130 NSString *reason; | |
131 if (accuracy) { | |
132 reason = | |
133 [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", | |
134 left, right, testDescription]; | |
135 } else { | |
136 reason = | |
137 [NSString stringWithFormat:@"'%@' should be equal to '%@' +/-'%@'. %@", | |
138 left, right, accuracy, testDescription]; | |
139 } | |
140 | |
141 return [self failureInFile:filename atLine:lineNumber reason:reason]; | |
142 } | |
143 | |
144 + (NSException *)failureInRaise:(NSString *)expression | |
145 inFile:(NSString *)filename | |
146 atLine:(int)lineNumber | |
147 withDescription:(NSString *)formatString, ... { | |
148 | |
149 NSString *testDescription = @""; | |
150 if (formatString) { | |
151 va_list vl; | |
152 va_start(vl, formatString); | |
153 testDescription = | |
154 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; | |
155 va_end(vl); | |
156 } | |
157 | |
158 NSString *reason = [NSString stringWithFormat:@"'%@' should raise. %@", | |
159 expression, testDescription]; | |
160 | |
161 return [self failureInFile:filename atLine:lineNumber reason:reason]; | |
162 } | |
163 | |
164 + (NSException *)failureInRaise:(NSString *)expression | |
165 exception:(NSException *)exception | |
166 inFile:(NSString *)filename | |
167 atLine:(int)lineNumber | |
168 withDescription:(NSString *)formatString, ... { | |
169 | |
170 NSString *testDescription = @""; | |
171 if (formatString) { | |
172 va_list vl; | |
173 va_start(vl, formatString); | |
174 testDescription = | |
175 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; | |
176 va_end(vl); | |
177 } | |
178 | |
179 NSString *reason; | |
180 if ([[exception name] isEqualToString:SenTestFailureException]) { | |
181 // it's our exception, assume it has the right description on it. | |
182 reason = [exception reason]; | |
183 } else { | |
184 // not one of our exception, use the exceptions reason and our description | |
185 reason = [NSString stringWithFormat:@"'%@' raised '%@'. %@", | |
186 expression, [exception reason], testDescription]; | |
187 } | |
188 | |
189 return [self failureInFile:filename atLine:lineNumber reason:reason]; | |
190 } | |
191 | |
192 @end | |
193 | |
194 NSString *STComposeString(NSString *formatString, ...) { | |
195 NSString *reason = @""; | |
196 if (formatString) { | |
197 va_list vl; | |
198 va_start(vl, formatString); | |
199 reason = | |
200 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; | |
201 va_end(vl); | |
202 } | |
203 return reason; | |
204 } | |
205 | |
206 NSString *const SenTestFailureException = @"SenTestFailureException"; | |
207 NSString *const SenTestFilenameKey = @"SenTestFilenameKey"; | |
208 NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey"; | |
209 | |
210 @interface SenTestCase (SenTestCasePrivate) | |
211 // our method of logging errors | |
212 + (void)printException:(NSException *)exception fromTestName:(NSString *)name; | |
213 @end | |
214 | |
215 @implementation SenTestCase | |
216 - (void)failWithException:(NSException*)exception { | |
217 [exception raise]; | |
218 } | |
219 | |
220 - (void)setUp { | |
221 } | |
222 | |
223 - (void)performTest:(SEL)sel { | |
224 currentSelector_ = sel; | |
225 @try { | |
226 [self invokeTest]; | |
227 } @catch (NSException *exception) { | |
228 [[self class] printException:exception | |
229 fromTestName:NSStringFromSelector(sel)]; | |
230 [exception raise]; | |
231 } | |
232 } | |
233 | |
234 + (void)printException:(NSException *)exception fromTestName:(NSString *)name { | |
235 NSDictionary *userInfo = [exception userInfo]; | |
236 NSString *filename = [userInfo objectForKey:SenTestFilenameKey]; | |
237 NSNumber *lineNumber = [userInfo objectForKey:SenTestLineNumberKey]; | |
238 NSString *className = NSStringFromClass([self class]); | |
239 if ([filename length] == 0) { | |
240 filename = @"Unknown.m"; | |
241 } | |
242 fprintf(stderr, "%s:%ld: error: -[%s %s] : %s\n", | |
243 [filename UTF8String], | |
244 (long)[lineNumber integerValue], | |
245 [className UTF8String], | |
246 [name UTF8String], | |
247 [[exception reason] UTF8String]); | |
248 fflush(stderr); | |
249 } | |
250 | |
251 - (void)invokeTest { | |
252 NSException *e = nil; | |
253 @try { | |
254 // Wrap things in autorelease pools because they may | |
255 // have an STMacro in their dealloc which may get called | |
256 // when the pool is cleaned up | |
257 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | |
258 // We don't log exceptions here, instead we let the person that called | |
259 // this log the exception. This ensures they are only logged once but the | |
260 // outer layers get the exceptions to report counts, etc. | |
261 @try { | |
262 [self setUp]; | |
263 @try { | |
264 [self performSelector:currentSelector_]; | |
265 } @catch (NSException *exception) { | |
266 e = [exception retain]; | |
267 } | |
268 [self tearDown]; | |
269 } @catch (NSException *exception) { | |
270 e = [exception retain]; | |
271 } | |
272 [pool release]; | |
273 } @catch (NSException *exception) { | |
274 e = [exception retain]; | |
275 } | |
276 if (e) { | |
277 [e autorelease]; | |
278 [e raise]; | |
279 } | |
280 } | |
281 | |
282 - (void)tearDown { | |
283 } | |
284 | |
285 - (NSString *)description { | |
286 // This matches the description OCUnit would return to you | |
287 return [NSString stringWithFormat:@"-[%@ %@]", [self class], | |
288 NSStringFromSelector(currentSelector_)]; | |
289 } | |
290 @end | |
291 | |
292 #endif // GTM_IPHONE_SDK | |
293 | |
294 @implementation GTMTestCase : SenTestCase | |
295 - (void)invokeTest { | |
296 Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog"); | |
297 if (devLogClass) { | |
298 [devLogClass performSelector:@selector(enableTracking)]; | |
299 [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; | |
300 | |
301 } | |
302 [super invokeTest]; | |
303 if (devLogClass) { | |
304 [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; | |
305 [devLogClass performSelector:@selector(disableTracking)]; | |
306 } | |
307 } | |
308 @end | |
309 | |
310 // Leak detection | |
311 #if !GTM_IPHONE_DEVICE | |
312 // Don't want to get leaks on the iPhone Device as the device doesn't | |
313 // have 'leaks'. The simulator does though. | |
314 | |
315 // COV_NF_START | |
316 // We don't have leak checking on by default, so this won't be hit. | |
317 static void _GTMRunLeaks(void) { | |
318 // This is an atexit handler. It runs leaks for us to check if we are | |
319 // leaking anything in our tests. | |
320 const char* cExclusionsEnv = getenv("GTM_LEAKS_SYMBOLS_TO_IGNORE"); | |
321 NSMutableString *exclusions = [NSMutableString string]; | |
322 if (cExclusionsEnv) { | |
323 NSString *exclusionsEnv = [NSString stringWithUTF8String:cExclusionsEnv]; | |
324 NSArray *exclusionsArray = [exclusionsEnv componentsSeparatedByString:@","]; | |
325 NSString *exclusion; | |
326 NSCharacterSet *wcSet = [NSCharacterSet whitespaceCharacterSet]; | |
327 GTM_FOREACH_OBJECT(exclusion, exclusionsArray) { | |
328 exclusion = [exclusion stringByTrimmingCharactersInSet:wcSet]; | |
329 [exclusions appendFormat:@"-exclude \"%@\" ", exclusion]; | |
330 } | |
331 } | |
332 NSString *string | |
333 = [NSString stringWithFormat:@"/usr/bin/leaks %@%d" | |
334 @"| /usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'", | |
335 exclusions, getpid()]; | |
336 int ret = system([string UTF8String]); | |
337 if (ret) { | |
338 fprintf(stderr, "%s:%d: Error: Unable to run leaks. 'system' returned: %d", | |
339 __FILE__, __LINE__, ret); | |
340 fflush(stderr); | |
341 } | |
342 } | |
343 // COV_NF_END | |
344 | |
345 static __attribute__((constructor)) void _GTMInstallLeaks(void) { | |
346 BOOL checkLeaks = YES; | |
347 #if !GTM_IPHONE_SDK | |
348 checkLeaks = GTMIsGarbageCollectionEnabled() ? NO : YES; | |
349 #endif // !GTM_IPHONE_SDK | |
350 if (checkLeaks) { | |
351 checkLeaks = getenv("GTM_ENABLE_LEAKS") ? YES : NO; | |
352 if (checkLeaks) { | |
353 // COV_NF_START | |
354 // We don't have leak checking on by default, so this won't be hit. | |
355 fprintf(stderr, "Leak Checking Enabled\n"); | |
356 fflush(stderr); | |
357 int ret = atexit(&_GTMRunLeaks); | |
358 _GTMDevAssert(ret == 0, | |
359 @"Unable to install _GTMRunLeaks as an atexit handler (%d)",
| |
360 errno); | |
361 // COV_NF_END | |
362 } | |
363 } | |
364 } | |
365 | |
366 #endif // !GTM_IPHONE_DEVICE | |
OLD | NEW |