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