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