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