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_time.h>
17#include <objc/objc.h>
18#include <objc/runtime.h>
19#include <objc/message.h>
20#include <objc/objc-auto.h>
21#include <TargetConditionals.h>
22
23static inline void succeed(const char *name)  __attribute__((noreturn));
24static inline void succeed(const char *name)
25{
26    if (name) {
27        char path[MAXPATHLEN+1];
28        strcpy(path, name);
29        fprintf(stderr, "OK: %s\n", basename(path));
30    } else {
31        fprintf(stderr, "OK\n");
32    }
33    exit(0);
34}
35
36static inline void fail(const char *msg, ...)   __attribute__((noreturn));
37static inline void fail(const char *msg, ...)
38{
39    va_list v;
40    if (msg) {
41        fprintf(stderr, "BAD: ");
42        va_start(v, msg);
43        vfprintf(stderr, msg, v);
44        va_end(v);
45        fprintf(stderr, "\n");
46    } else {
47        fprintf(stderr, "BAD\n");
48    }
49    exit(1);
50}
51
52#define testassert(cond) \
53    ((void) ((cond) ? (void)0 : __testassert(#cond, __FILE__, __LINE__)))
54#define __testassert(cond, file, line) \
55    (fail("failed assertion '%s' at %s:%u", cond, __FILE__, __LINE__))
56
57/* time-sensitive assertion, disabled under valgrind */
58#define timecheck(name, time, fast, slow)                                    \
59    if (getenv("VALGRIND") && 0 != strcmp(getenv("VALGRIND"), "NO")) {  \
60        /* valgrind; do nothing */                                      \
61    } else if (time > slow) {                                           \
62        fprintf(stderr, "SLOW: %s %llu, expected %llu..%llu\n",         \
63                name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
64    } else if (time < fast) {                                           \
65        fprintf(stderr, "FAST: %s %llu, expected %llu..%llu\n",         \
66                name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
67    } else {                                                            \
68        testprintf("time: %s %llu, expected %llu..%llu\n",              \
69                   name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
70    }
71
72
73static inline void testprintf(const char *msg, ...)
74{
75    if (msg  &&  getenv("VERBOSE")) {
76        va_list v;
77        va_start(v, msg);
78        fprintf(stderr, "VERBOSE: ");
79        vfprintf(stderr, msg, v);
80        va_end(v);
81    }
82}
83
84// complain to output, but don't fail the test
85// Use when warning that some test is being temporarily skipped
86// because of something like a compiler bug.
87static inline void testwarn(const char *msg, ...)
88{
89    if (msg) {
90        va_list v;
91        va_start(v, msg);
92        fprintf(stderr, "WARN: ");
93        vfprintf(stderr, msg, v);
94        va_end(v);
95        fprintf(stderr, "\n");
96    }
97}
98
99
100// Run GC. This is a macro to reach as high in the stack as possible.
101#ifndef OBJC_NO_GC
102#   define testcollect()                                                \
103        do {                                                            \
104            if (objc_collectingEnabled()) {                             \
105                objc_clear_stack(0);                                    \
106                objc_collect(OBJC_COLLECT_IF_NEEDED|OBJC_WAIT_UNTIL_DONE); \
107                objc_collect(OBJC_EXHAUSTIVE_COLLECTION|OBJC_WAIT_UNTIL_DONE);\
108                objc_collect(OBJC_EXHAUSTIVE_COLLECTION|OBJC_WAIT_UNTIL_DONE);\
109            }                                                           \
110        } while (0)
111#else
112#   define testcollect() do { } while (0)
113#endif
114
115/* Leak checking
116   Fails if total malloc memory in use at leak_check(n)
117   is more than n bytes above that at leak_mark().
118
119   fixme rdar://8437289 malloc_zone_statistics(auto_zone()) lies
120*/
121
122static size_t _leak_start;
123static inline void leak_mark(void)
124{
125    if (objc_collectingEnabled()) return;
126    malloc_statistics_t stats;
127    testcollect();
128    malloc_zone_statistics(NULL, &stats);
129    _leak_start = stats.size_in_use;
130}
131
132#define leak_check(n)                                                   \
133    do {                                                                \
134        if (objc_collectingEnabled()) break;                            \
135        const char *_check = getenv("LEAK_CHECK");                      \
136        if (_check && 0 == strcmp(_check, "NO")) break;                 \
137        testcollect();                                                  \
138        malloc_statistics_t stats;                                      \
139        malloc_zone_statistics(NULL, &stats);                           \
140        if (stats.size_in_use > _leak_start + n) {                      \
141            if (getenv("HANG_ON_LEAK")) {                               \
142                printf("leaks %d\n", getpid());                         \
143                while (1) sleep(1);                                     \
144            }                                                           \
145            fail("%zu bytes leaked at %s:%u",                           \
146                 stats.size_in_use - _leak_start, __FILE__, __LINE__);  \
147        }                                                               \
148    } while (0)
149
150static inline bool is_guardmalloc(void)
151{
152    const char *env = getenv("GUARDMALLOC");
153    return (env  &&  0 == strcmp(env, "YES"));
154}
155
156#endif
157