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
27static inline void succeed(const char *name)  __attribute__((noreturn));
28static 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
40static inline void fail(const char *msg, ...)   __attribute__((noreturn));
41static 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
78static 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.
94static 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
107static 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
158typedef void(^testblock_t)(void);
159static __unsafe_unretained testblock_t testcodehack;
160static inline void *_testthread(void *arg __unused)
161{
162    objc_registerThreadWithCollector();
163    testcodehack();
164    return NULL;
165}
166static 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>
180inline void* operator new(std::size_t) throw (std::bad_alloc) { fail("called global operator new"); }
181inline void* operator new[](std::size_t) throw (std::bad_alloc) { fail("called global operator new[]"); }
182inline void* operator new(std::size_t, const std::nothrow_t&) throw() { fail("called global operator new(nothrow)"); }
183inline void* operator new[](std::size_t, const std::nothrow_t&) throw() { fail("called global operator new[](nothrow)"); }
184inline void operator delete(void*) throw() { fail("called global operator delete"); }
185inline void operator delete[](void*) throw() { fail("called global operator delete[]"); }
186inline void operator delete(void*, const std::nothrow_t&) throw() { fail("called global operator delete(nothrow)"); }
187inline 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
196static 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
204static 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
223static 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
241static size_t _leak_start;
242static 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
271static 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
280static id self_fn(id x) __attribute__((used));
281static 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
328OBJC_ROOT_CLASS
329@interface TestRoot {
330 @public
331    Class isa;
332}
333
334+(void) load;
335+(void) initialize;
336
337-(id) self;
338-(Class) class;
339-(Class) superclass;
340
341+(id) new;
342+(id) alloc;
343+(id) allocWithZone:(void*)zone;
344-(id) copy;
345-(id) mutableCopy;
346-(id) init;
347-(void) dealloc;
348-(void) finalize;
349@end
350@interface TestRoot (RR)
351-(id) retain;
352-(oneway void) release;
353-(id) autorelease;
354-(unsigned long) retainCount;
355-(id) copyWithZone:(void *)zone;
356-(id) mutableCopyWithZone:(void*)zone;
357@end
358
359// incremented for each call of TestRoot's methods
360extern int TestRootLoad;
361extern int TestRootInitialize;
362extern int TestRootAlloc;
363extern int TestRootAllocWithZone;
364extern int TestRootCopy;
365extern int TestRootCopyWithZone;
366extern int TestRootMutableCopy;
367extern int TestRootMutableCopyWithZone;
368extern int TestRootInit;
369extern int TestRootDealloc;
370extern int TestRootFinalize;
371extern int TestRootRetain;
372extern int TestRootRelease;
373extern int TestRootAutorelease;
374extern int TestRootRetainCount;
375extern int TestRootTryRetain;
376extern int TestRootIsDeallocating;
377extern int TestRootPlusRetain;
378extern int TestRootPlusRelease;
379extern int TestRootPlusAutorelease;
380extern int TestRootPlusRetainCount;
381
382#endif
383
384
385// Struct that does not return in registers on any architecture
386
387struct stret {
388    int a;
389    int b;
390    int c;
391    int d;
392    int e;
393    int f;
394    int g;
395    int h;
396    int i;
397    int j;
398};
399
400static inline BOOL stret_equal(struct stret a, struct stret b)
401{
402    return (a.a == b.a  &&
403            a.b == b.b  &&
404            a.c == b.c  &&
405            a.d == b.d  &&
406            a.e == b.e  &&
407            a.f == b.f  &&
408            a.g == b.g  &&
409            a.h == b.h  &&
410            a.i == b.i  &&
411            a.j == b.j);
412}
413
414static struct stret STRET_RESULT __attribute__((used)) = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
415
416#endif
417