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