]> git.saurik.com Git - apple/objc4.git/blob - test/test.h
objc4-493.9.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 <sys/param.h>
15 #include <malloc/malloc.h>
16 #include <mach/mach.h>
17 #include <mach/mach_time.h>
18 #include <objc/objc.h>
19 #include <objc/runtime.h>
20 #include <objc/message.h>
21 #include <objc/objc-auto.h>
22 #include <TargetConditionals.h>
23
24 static inline void succeed(const char *name) __attribute__((noreturn));
25 static inline void succeed(const char *name)
26 {
27 if (name) {
28 char path[MAXPATHLEN+1];
29 strcpy(path, name);
30 fprintf(stderr, "OK: %s\n", basename(path));
31 } else {
32 fprintf(stderr, "OK\n");
33 }
34 exit(0);
35 }
36
37 static inline void fail(const char *msg, ...) __attribute__((noreturn));
38 static inline void fail(const char *msg, ...)
39 {
40 va_list v;
41 if (msg) {
42 fprintf(stderr, "BAD: ");
43 va_start(v, msg);
44 vfprintf(stderr, msg, v);
45 va_end(v);
46 fprintf(stderr, "\n");
47 } else {
48 fprintf(stderr, "BAD\n");
49 }
50 exit(1);
51 }
52
53 #define testassert(cond) \
54 ((void) ((cond) ? (void)0 : __testassert(#cond, __FILE__, __LINE__)))
55 #define __testassert(cond, file, line) \
56 (fail("failed assertion '%s' at %s:%u", cond, __FILE__, __LINE__))
57
58 /* time-sensitive assertion, disabled under valgrind */
59 #define timecheck(name, time, fast, slow) \
60 if (getenv("VALGRIND") && 0 != strcmp(getenv("VALGRIND"), "NO")) { \
61 /* valgrind; do nothing */ \
62 } else if (time > slow) { \
63 fprintf(stderr, "SLOW: %s %llu, expected %llu..%llu\n", \
64 name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
65 } else if (time < fast) { \
66 fprintf(stderr, "FAST: %s %llu, expected %llu..%llu\n", \
67 name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
68 } else { \
69 testprintf("time: %s %llu, expected %llu..%llu\n", \
70 name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
71 }
72
73
74 static inline void testprintf(const char *msg, ...)
75 {
76 if (msg && getenv("VERBOSE")) {
77 va_list v;
78 va_start(v, msg);
79 fprintf(stderr, "VERBOSE: ");
80 vfprintf(stderr, msg, v);
81 va_end(v);
82 }
83 }
84
85 // complain to output, but don't fail the test
86 // Use when warning that some test is being temporarily skipped
87 // because of something like a compiler bug.
88 static inline void testwarn(const char *msg, ...)
89 {
90 if (msg) {
91 va_list v;
92 va_start(v, msg);
93 fprintf(stderr, "WARN: ");
94 vfprintf(stderr, msg, v);
95 va_end(v);
96 fprintf(stderr, "\n");
97 }
98 }
99
100 static inline void testnoop() { }
101
102 // Run GC. This is a macro to reach as high in the stack as possible.
103 #ifndef OBJC_NO_GC
104
105 # if __OBJC2__
106 # define testexc()
107 # else
108 # include <objc/objc-exception.h>
109 # define testexc() \
110 do { \
111 objc_exception_functions_t table = {0,0,0,0,0,0}; \
112 objc_exception_get_functions(&table); \
113 if (!table.throw_exc) { \
114 table.throw_exc = (typeof(table.throw_exc))abort; \
115 table.try_enter = (typeof(table.try_enter))testnoop; \
116 table.try_exit = (typeof(table.try_exit))testnoop; \
117 table.extract = (typeof(table.extract))abort; \
118 table.match = (typeof(table.match))abort; \
119 objc_exception_set_functions(&table); \
120 } \
121 } while (0)
122 # endif
123
124 # define testcollect() \
125 do { \
126 if (objc_collectingEnabled()) { \
127 testexc(); \
128 objc_clear_stack(0); \
129 objc_collect(OBJC_COLLECT_IF_NEEDED|OBJC_WAIT_UNTIL_DONE); \
130 objc_collect(OBJC_EXHAUSTIVE_COLLECTION|OBJC_WAIT_UNTIL_DONE);\
131 objc_collect(OBJC_EXHAUSTIVE_COLLECTION|OBJC_WAIT_UNTIL_DONE);\
132 } \
133 _objc_flush_caches(NULL); \
134 } while (0)
135
136 #else
137
138 # define testcollect() \
139 do { \
140 _objc_flush_caches(NULL); \
141 } while (0)
142
143 #endif
144
145
146 /* Make sure libobjc does not call global operator new.
147 Any test that DOES need to call global operator new must
148 `#define TEST_CALLS_OPERATOR_NEW` before including test.h.
149 */
150 #if __cplusplus && !defined(TEST_CALLS_OPERATOR_NEW)
151 #import <new>
152 inline void* operator new(std::size_t) throw (std::bad_alloc) { fail("called global operator new"); }
153 inline void* operator new[](std::size_t) throw (std::bad_alloc) { fail("called global operator new[]"); }
154 inline void* operator new(std::size_t, const std::nothrow_t&) throw() { fail("called global operator new(nothrow)"); }
155 inline void* operator new[](std::size_t, const std::nothrow_t&) throw() { fail("called global operator new[](nothrow)"); }
156 inline void operator delete(void*) throw() { fail("called global operator delete"); }
157 inline void operator delete[](void*) throw() { fail("called global operator delete[]"); }
158 inline void operator delete(void*, const std::nothrow_t&) throw() { fail("called global operator delete(nothrow)"); }
159 inline void operator delete[](void*, const std::nothrow_t&) throw() { fail("called global operator delete[](nothrow)"); }
160 #endif
161
162
163 /* Leak checking
164 Fails if total malloc memory in use at leak_check(n)
165 is more than n bytes above that at leak_mark().
166 */
167
168 static inline void leak_recorder(task_t task __unused, void *ctx, unsigned type __unused, vm_range_t *ranges, unsigned count)
169 {
170 size_t *inuse = (size_t *)ctx;
171 while (count--) {
172 *inuse += ranges[count].size;
173 }
174 }
175
176 static inline size_t leak_inuse(void)
177 {
178 size_t total = 0;
179 vm_address_t *zones;
180 unsigned count;
181 malloc_get_all_zones(mach_task_self(), NULL, &zones, &count);
182 for (unsigned i = 0; i < count; i++) {
183 size_t inuse = 0;
184 malloc_zone_t *zone = (malloc_zone_t *)zones[i];
185 if (!zone->introspect || !zone->introspect->enumerator) continue;
186
187 zone->introspect->enumerator(mach_task_self(), &inuse, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)zone, NULL, leak_recorder);
188 total += inuse;
189 }
190
191 return total;
192 }
193
194
195 static inline void leak_dump_heap(const char *msg)
196 {
197 fprintf(stderr, "%s\n", msg);
198
199 // Make `heap` write to stderr
200 int outfd = dup(STDOUT_FILENO);
201 dup2(STDERR_FILENO, STDOUT_FILENO);
202 pid_t pid = getpid();
203 char cmd[256];
204 // environment variables reset for iOS simulator use
205 sprintf(cmd, "DYLD_LIBRARY_PATH= DYLD_ROOT_PATH= /usr/bin/heap -addresses all %d", (int)pid);
206
207 system(cmd);
208
209 dup2(outfd, STDOUT_FILENO);
210 close(outfd);
211 }
212
213 static size_t _leak_start;
214 static inline void leak_mark(void)
215 {
216 testcollect();
217 if (getenv("LEAK_HEAP")) {
218 leak_dump_heap("HEAP AT leak_mark");
219 }
220 _leak_start = leak_inuse();
221 }
222
223 #define leak_check(n) \
224 do { \
225 const char *_check = getenv("LEAK_CHECK"); \
226 size_t inuse; \
227 if (_check && 0 == strcmp(_check, "NO")) break; \
228 testcollect(); \
229 if (getenv("LEAK_HEAP")) { \
230 leak_dump_heap("HEAP AT leak_check"); \
231 } \
232 inuse = leak_inuse(); \
233 if (inuse > _leak_start + n) { \
234 if (getenv("HANG_ON_LEAK")) { \
235 printf("leaks %d\n", getpid()); \
236 while (1) sleep(1); \
237 } \
238 fprintf(stderr, "BAD: %zu bytes leaked at %s:%u\n", \
239 inuse - _leak_start, __FILE__, __LINE__); \
240 } \
241 } while (0)
242
243 static inline bool is_guardmalloc(void)
244 {
245 const char *env = getenv("GUARDMALLOC");
246 return (env && 0 == strcmp(env, "YES"));
247 }
248
249 #endif