]> git.saurik.com Git - apple/objc4.git/blob - test/test.h
c3abfb0a5b9836ad0263ccb56d967ed449291e17
[apple/objc4.git] / test / test.h
1 // test.h
2 // Common definitions for trivial test harness
3
4
5 #ifndef TEST_H
6 #define TEST_H
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11 #include <string.h>
12 #include <libgen.h>
13 #include <unistd.h>
14 #include <pthread.h>
15 #include <sys/param.h>
16 #include <malloc/malloc.h>
17 #include <mach/mach.h>
18 #include <mach/vm_param.h>
19 #include <mach/mach_time.h>
20 #include <objc/objc.h>
21 #include <objc/runtime.h>
22 #include <objc/message.h>
23 #include <objc/objc-abi.h>
24 #include <objc/objc-auto.h>
25 #include <objc/objc-internal.h>
26 #include <TargetConditionals.h>
27
28 // Configuration macros
29
30 #if !__LP64__ || TARGET_OS_WIN32 || __OBJC_GC__ || TARGET_IPHONE_SIMULATOR || (TARGET_OS_MAC && !TARGET_OS_IPHONE)
31 # define SUPPORT_NONPOINTER_ISA 0
32 #elif __x86_64__
33 # define SUPPORT_NONPOINTER_ISA 1
34 #elif __arm64__
35 # define SUPPORT_NONPOINTER_ISA 1
36 #else
37 # error unknown architecture
38 #endif
39
40
41 // Test output
42
43 static inline void succeed(const char *name) __attribute__((noreturn));
44 static inline void succeed(const char *name)
45 {
46 if (name) {
47 char path[MAXPATHLEN+1];
48 strcpy(path, name);
49 fprintf(stderr, "OK: %s\n", basename(path));
50 } else {
51 fprintf(stderr, "OK\n");
52 }
53 exit(0);
54 }
55
56 static inline void fail(const char *msg, ...) __attribute__((noreturn));
57 static inline void fail(const char *msg, ...)
58 {
59 if (msg) {
60 char *msg2;
61 asprintf(&msg2, "BAD: %s\n", msg);
62 va_list v;
63 va_start(v, msg);
64 vfprintf(stderr, msg2, v);
65 va_end(v);
66 free(msg2);
67 } else {
68 fprintf(stderr, "BAD\n");
69 }
70 exit(1);
71 }
72
73 #define testassert(cond) \
74 ((void) (((cond) != 0) ? (void)0 : __testassert(#cond, __FILE__, __LINE__)))
75 #define __testassert(cond, file, line) \
76 (fail("failed assertion '%s' at %s:%u", cond, __FILE__, __LINE__))
77
78 /* time-sensitive assertion, disabled under valgrind */
79 #define timecheck(name, time, fast, slow) \
80 if (getenv("VALGRIND") && 0 != strcmp(getenv("VALGRIND"), "NO")) { \
81 /* valgrind; do nothing */ \
82 } else if (time > slow) { \
83 fprintf(stderr, "SLOW: %s %llu, expected %llu..%llu\n", \
84 name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
85 } else if (time < fast) { \
86 fprintf(stderr, "FAST: %s %llu, expected %llu..%llu\n", \
87 name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
88 } else { \
89 testprintf("time: %s %llu, expected %llu..%llu\n", \
90 name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
91 }
92
93
94 static inline void testprintf(const char *msg, ...)
95 {
96 if (msg && getenv("VERBOSE")) {
97 char *msg2;
98 asprintf(&msg2, "VERBOSE: %s", msg);
99 va_list v;
100 va_start(v, msg);
101 vfprintf(stderr, msg2, v);
102 va_end(v);
103 free(msg2);
104 }
105 }
106
107 // complain to output, but don't fail the test
108 // Use when warning that some test is being temporarily skipped
109 // because of something like a compiler bug.
110 static inline void testwarn(const char *msg, ...)
111 {
112 if (msg) {
113 char *msg2;
114 asprintf(&msg2, "WARN: %s\n", msg);
115 va_list v;
116 va_start(v, msg);
117 vfprintf(stderr, msg2, v);
118 va_end(v);
119 free(msg2);
120 }
121 }
122
123 static inline void testnoop() { }
124
125 // Run GC. This is a macro to reach as high in the stack as possible.
126 #ifndef OBJC_NO_GC
127
128 # if __OBJC2__
129 # define testexc()
130 # else
131 # include <objc/objc-exception.h>
132 # define testexc() \
133 do { \
134 objc_exception_functions_t table = {0,0,0,0,0,0}; \
135 objc_exception_get_functions(&table); \
136 if (!table.throw_exc) { \
137 table.throw_exc = (typeof(table.throw_exc))abort; \
138 table.try_enter = (typeof(table.try_enter))testnoop; \
139 table.try_exit = (typeof(table.try_exit))testnoop; \
140 table.extract = (typeof(table.extract))abort; \
141 table.match = (typeof(table.match))abort; \
142 objc_exception_set_functions(&table); \
143 } \
144 } while (0)
145 # endif
146
147 # define testcollect() \
148 do { \
149 if (objc_collectingEnabled()) { \
150 testexc(); \
151 objc_clear_stack(0); \
152 objc_collect(OBJC_COLLECT_IF_NEEDED|OBJC_WAIT_UNTIL_DONE); \
153 objc_collect(OBJC_EXHAUSTIVE_COLLECTION|OBJC_WAIT_UNTIL_DONE);\
154 objc_collect(OBJC_EXHAUSTIVE_COLLECTION|OBJC_WAIT_UNTIL_DONE);\
155 } \
156 _objc_flush_caches(NULL); \
157 } while (0)
158
159 #else
160
161 # define testcollect() \
162 do { \
163 _objc_flush_caches(NULL); \
164 } while (0)
165
166 #endif
167
168
169 // Synchronously run test code on another thread.
170 // This can help force GC to kill objects promptly, which some tests depend on.
171
172 // The block object is unsafe_unretained because we must not allow
173 // ARC to retain them in non-Foundation tests
174 typedef void(^testblock_t)(void);
175 static __unsafe_unretained testblock_t testcodehack;
176 static inline void *_testthread(void *arg __unused)
177 {
178 objc_registerThreadWithCollector();
179 testcodehack();
180 return NULL;
181 }
182 static inline void testonthread(__unsafe_unretained testblock_t code)
183 {
184 pthread_t th;
185 testcodehack = code; // force GC not-thread-local, avoid ARC void* casts
186 pthread_create(&th, NULL, _testthread, NULL);
187 pthread_join(th, NULL);
188 }
189
190 /* Make sure libobjc does not call global operator new.
191 Any test that DOES need to call global operator new must
192 `#define TEST_CALLS_OPERATOR_NEW` before including test.h.
193 */
194 #if __cplusplus && !defined(TEST_CALLS_OPERATOR_NEW)
195 #pragma clang diagnostic push
196 #pragma clang diagnostic ignored "-Winline-new-delete"
197 #import <new>
198 inline void* operator new(std::size_t) throw (std::bad_alloc) { fail("called global operator new"); }
199 inline void* operator new[](std::size_t) throw (std::bad_alloc) { fail("called global operator new[]"); }
200 inline void* operator new(std::size_t, const std::nothrow_t&) throw() { fail("called global operator new(nothrow)"); }
201 inline void* operator new[](std::size_t, const std::nothrow_t&) throw() { fail("called global operator new[](nothrow)"); }
202 inline void operator delete(void*) throw() { fail("called global operator delete"); }
203 inline void operator delete[](void*) throw() { fail("called global operator delete[]"); }
204 inline void operator delete(void*, const std::nothrow_t&) throw() { fail("called global operator delete(nothrow)"); }
205 inline void operator delete[](void*, const std::nothrow_t&) throw() { fail("called global operator delete[](nothrow)"); }
206 #pragma clang diagnostic pop
207 #endif
208
209
210 /* Leak checking
211 Fails if total malloc memory in use at leak_check(n)
212 is more than n bytes above that at leak_mark().
213 */
214
215 static inline void leak_recorder(task_t task __unused, void *ctx, unsigned type __unused, vm_range_t *ranges, unsigned count)
216 {
217 size_t *inuse = (size_t *)ctx;
218 while (count--) {
219 *inuse += ranges[count].size;
220 }
221 }
222
223 static inline size_t leak_inuse(void)
224 {
225 size_t total = 0;
226 vm_address_t *zones;
227 unsigned count;
228 malloc_get_all_zones(mach_task_self(), NULL, &zones, &count);
229 for (unsigned i = 0; i < count; i++) {
230 size_t inuse = 0;
231 malloc_zone_t *zone = (malloc_zone_t *)zones[i];
232 if (!zone->introspect || !zone->introspect->enumerator) continue;
233
234 // skip DispatchContinuations because it sometimes claims to be
235 // using lots of memory that then goes away later
236 if (0 == strcmp(zone->zone_name, "DispatchContinuations")) continue;
237
238 zone->introspect->enumerator(mach_task_self(), &inuse, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)zone, NULL, leak_recorder);
239 // fprintf(stderr, "%zu in use for zone %s\n", inuse, zone->zone_name);
240 total += inuse;
241 }
242
243 return total;
244 }
245
246
247 static inline void leak_dump_heap(const char *msg)
248 {
249 fprintf(stderr, "%s\n", msg);
250
251 // Make `heap` write to stderr
252 int outfd = dup(STDOUT_FILENO);
253 dup2(STDERR_FILENO, STDOUT_FILENO);
254 pid_t pid = getpid();
255 char cmd[256];
256 // environment variables reset for iOS simulator use
257 sprintf(cmd, "DYLD_LIBRARY_PATH= DYLD_ROOT_PATH= /usr/bin/heap -addresses all %d", (int)pid);
258
259 system(cmd);
260
261 dup2(outfd, STDOUT_FILENO);
262 close(outfd);
263 }
264
265 static size_t _leak_start;
266 static inline void leak_mark(void)
267 {
268 testcollect();
269 if (getenv("LEAK_HEAP")) {
270 leak_dump_heap("HEAP AT leak_mark");
271 }
272 _leak_start = leak_inuse();
273 }
274
275 #define leak_check(n) \
276 do { \
277 const char *_check = getenv("LEAK_CHECK"); \
278 size_t inuse; \
279 if (_check && 0 == strcmp(_check, "NO")) break; \
280 testcollect(); \
281 if (getenv("LEAK_HEAP")) { \
282 leak_dump_heap("HEAP AT leak_check"); \
283 } \
284 inuse = leak_inuse(); \
285 if (inuse > _leak_start + n) { \
286 if (getenv("HANG_ON_LEAK")) { \
287 printf("leaks %d\n", getpid()); \
288 while (1) sleep(1); \
289 } \
290 fprintf(stderr, "BAD: %zu bytes leaked at %s:%u\n", \
291 inuse - _leak_start, __FILE__, __LINE__); \
292 } \
293 } while (0)
294
295 static inline bool is_guardmalloc(void)
296 {
297 const char *env = getenv("GUARDMALLOC");
298 return (env && 0 == strcmp(env, "YES"));
299 }
300
301
302 /* Memory management compatibility macros */
303
304 static id self_fn(id x) __attribute__((used));
305 static id self_fn(id x) { return x; }
306
307 #if __has_feature(objc_arc)
308 // ARC
309 # define RELEASE_VAR(x) x = nil
310 # define WEAK_STORE(dst, val) (dst = (val))
311 # define WEAK_LOAD(src) (src)
312 # define SUPER_DEALLOC()
313 # define RETAIN(x) (self_fn(x))
314 # define RELEASE_VALUE(x) ((void)self_fn(x))
315 # define AUTORELEASE(x) (self_fn(x))
316
317 #elif defined(__OBJC_GC__)
318 // GC
319 # define RELEASE_VAR(x) x = nil
320 # define WEAK_STORE(dst, val) (dst = (val))
321 # define WEAK_LOAD(src) (src)
322 # define SUPER_DEALLOC() [super dealloc]
323 # define RETAIN(x) [x self]
324 # define RELEASE_VALUE(x) (void)[x self]
325 # define AUTORELEASE(x) [x self]
326
327 #else
328 // MRC
329 # define RELEASE_VAR(x) do { [x release]; x = nil; } while (0)
330 # define WEAK_STORE(dst, val) objc_storeWeak((id *)&dst, val)
331 # define WEAK_LOAD(src) objc_loadWeak((id *)&src)
332 # define SUPER_DEALLOC() [super dealloc]
333 # define RETAIN(x) [x retain]
334 # define RELEASE_VALUE(x) [x release]
335 # define AUTORELEASE(x) [x autorelease]
336 #endif
337
338 /* gcc compatibility macros */
339 /* <rdar://problem/9412038> @autoreleasepool should generate objc_autoreleasePoolPush/Pop on 10.7/5.0 */
340 //#if !defined(__clang__)
341 # define PUSH_POOL { void *pool = objc_autoreleasePoolPush();
342 # define POP_POOL objc_autoreleasePoolPop(pool); }
343 //#else
344 //# define PUSH_POOL @autoreleasepool
345 //# define POP_POOL
346 //#endif
347
348 #if __OBJC__
349
350 /* General purpose root class */
351
352 OBJC_ROOT_CLASS
353 @interface TestRoot {
354 @public
355 Class isa;
356 }
357
358 +(void) load;
359 +(void) initialize;
360
361 -(id) self;
362 -(Class) class;
363 -(Class) superclass;
364
365 +(id) new;
366 +(id) alloc;
367 +(id) allocWithZone:(void*)zone;
368 -(id) copy;
369 -(id) mutableCopy;
370 -(id) init;
371 -(void) dealloc;
372 -(void) finalize;
373 @end
374 @interface TestRoot (RR)
375 -(id) retain;
376 -(oneway void) release;
377 -(id) autorelease;
378 -(unsigned long) retainCount;
379 -(id) copyWithZone:(void *)zone;
380 -(id) mutableCopyWithZone:(void*)zone;
381 @end
382
383 // incremented for each call of TestRoot's methods
384 extern int TestRootLoad;
385 extern int TestRootInitialize;
386 extern int TestRootAlloc;
387 extern int TestRootAllocWithZone;
388 extern int TestRootCopy;
389 extern int TestRootCopyWithZone;
390 extern int TestRootMutableCopy;
391 extern int TestRootMutableCopyWithZone;
392 extern int TestRootInit;
393 extern int TestRootDealloc;
394 extern int TestRootFinalize;
395 extern int TestRootRetain;
396 extern int TestRootRelease;
397 extern int TestRootAutorelease;
398 extern int TestRootRetainCount;
399 extern int TestRootTryRetain;
400 extern int TestRootIsDeallocating;
401 extern int TestRootPlusRetain;
402 extern int TestRootPlusRelease;
403 extern int TestRootPlusAutorelease;
404 extern int TestRootPlusRetainCount;
405
406 #endif
407
408
409 // Struct that does not return in registers on any architecture
410
411 struct stret {
412 int a;
413 int b;
414 int c;
415 int d;
416 int e;
417 int f;
418 int g;
419 int h;
420 int i;
421 int j;
422 };
423
424 static inline BOOL stret_equal(struct stret a, struct stret b)
425 {
426 return (a.a == b.a &&
427 a.b == b.b &&
428 a.c == b.c &&
429 a.d == b.d &&
430 a.e == b.e &&
431 a.f == b.f &&
432 a.g == b.g &&
433 a.h == b.h &&
434 a.i == b.i &&
435 a.j == b.j);
436 }
437
438 static struct stret STRET_RESULT __attribute__((used)) = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
439
440 #endif