dyld-832.7.1.tar.gz
[apple/dyld.git] / testing / lib / test_support.cpp
1 #include <dlfcn.h>
2 #include <Block.h>
3 #include <spawn.h>
4 #include <stdio.h>
5 #include <assert.h>
6 #include <signal.h>
7 #include <stdarg.h>
8 #include <stdbool.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <os/lock.h>
14 #include <sys/attr.h>
15 #include <sys/wait.h>
16 #include <mach/mach.h>
17 #include <mach-o/dyld.h>
18 #include <mach/mach_vm.h>
19 #include <sys/fsgetpath.h>
20 #include <mach-o/getsect.h>
21 #include <mach/vm_region.h>
22 #include <dispatch/private.h>
23 #include <dispatch/dispatch.h>
24
25
26 #include <atomic>
27 #include <utility>
28 #include <algorithm>
29
30 extern "C" {
31 #include "execserverServer.h"
32
33 union catch_mach_exc_request_reply {
34 union __RequestUnion__catch_mach_exc_subsystem request;
35 union __ReplyUnion__catch_mach_exc_subsystem reply;
36 };
37 };
38
39 #include "test_support.h"
40
41 extern const int NXArgc;
42 extern const char** NXArgv;
43 extern const char** environ;
44 extern char* __progname;
45 #if __x86_64__
46 static const cpu_type_t currentArch = CPU_TYPE_X86_64;
47 #elif __i386__
48 static const cpu_type_t currentArch = CPU_TYPE_I386;
49 #elif __arm64__
50 static const cpu_type_t currentArch = CPU_TYPE_ARM64;
51 #elif __arm__
52 static const cpu_type_t currentArch = CPU_TYPE_ARM;
53 #endif
54
55 namespace {
56 struct ScopedLock {
57 ScopedLock() : _lock(OS_UNFAIR_LOCK_INIT) {}
58 template<typename F>
59 void withLock(F f) {
60 os_unfair_lock_lock(&_lock);
61 f();
62 os_unfair_lock_unlock(&_lock);
63 }
64 private:
65 os_unfair_lock _lock;
66 };
67
68 template <typename T, int QUANT=4, int INIT=1>
69 class GrowableArray
70 {
71 public:
72
73 T& operator[](size_t idx) { assert(idx < _usedCount); return _elements[idx]; }
74 const T& operator[](size_t idx) const { assert(idx < _usedCount); return _elements[idx]; }
75 T& back() { assert(_usedCount > 0); return _elements[_usedCount-1]; }
76 uintptr_t count() const { return _usedCount; }
77 uintptr_t maxCount() const { return _allocCount; }
78 bool empty() const { return (_usedCount == 0); }
79 uintptr_t index(const T& element) { return &element - _elements; }
80 void push_back(const T& t) { verifySpace(1); _elements[_usedCount++] = t; }
81 void pop_back() { assert(_usedCount > 0); _usedCount--; }
82 T* begin() { return &_elements[0]; }
83 T* end() { return &_elements[_usedCount]; }
84 const T* begin() const { return &_elements[0]; }
85 const T* end() const { return &_elements[_usedCount]; }
86 bool contains(const T& targ) const { for (const T& a : *this) { if ( a == targ ) return true; } return false; }
87 void erase(T& targ);
88
89 protected:
90 void growTo(uintptr_t n);
91 void verifySpace(uintptr_t n) { if (this->_usedCount+n > this->_allocCount) growTo(this->_usedCount + n); }
92
93 private:
94 T* _elements = _initialAlloc;
95 uintptr_t _allocCount = INIT;
96 uintptr_t _usedCount = 0;
97 T _initialAlloc[INIT] = { };
98 };
99
100
101 template <typename T, int QUANT, int INIT>
102 inline void GrowableArray<T,QUANT,INIT>::growTo(uintptr_t n)
103 {
104 uintptr_t newCount = (n + QUANT - 1) & (-QUANT);
105 T* newArray = (T*)::malloc(sizeof(T)*newCount);
106 T* oldArray = this->_elements;
107 if ( this->_usedCount != 0 )
108 ::memcpy(newArray, oldArray, sizeof(T)*this->_usedCount);
109 this->_elements = newArray;
110 this->_allocCount = newCount;
111 if ( oldArray != this->_initialAlloc )
112 ::free(oldArray);
113 }
114
115 template <typename T, int QUANT, int INIT>
116 inline void GrowableArray<T,QUANT,INIT>::erase(T& targ)
117 {
118 intptr_t index = &targ - _elements;
119 assert(index >= 0);
120 assert(index < (intptr_t)_usedCount);
121 intptr_t moveCount = _usedCount-index-1;
122 if ( moveCount > 0 )
123 ::memcpy(&_elements[index], &_elements[index+1], moveCount*sizeof(T));
124 _usedCount -= 1;
125 }
126
127 struct TestState {
128 TestState();
129 static TestState* getState();
130 void _PASSV(const char* file, unsigned line, const char* format, va_list args) __attribute__ ((noreturn));
131 void _FAILV(const char* file, unsigned line, const char* format, va_list args) __attribute__ ((noreturn));
132 void _LOGV(const char* file, unsigned line, const char* format, va_list args);
133 GrowableArray<std::pair<mach_port_t, _dyld_test_crash_handler_t>>& getCrashHandlers();
134 private:
135 enum OutputStyle {
136 None,
137 BATS,
138 Console,
139 XCTest
140 };
141 void runLeaks();
142 void dumpLogs();
143 void getLogsString(char** buffer);
144 static uint8_t hexCharToUInt(const char hexByte, uint8_t* value);
145 static uint64_t hexToUInt64(const char* startHexByte, const char** endHexByte);
146
147 ScopedLock _IOlock;
148 GrowableArray<const char *> logs;
149 const char *testName;
150 bool logImmediate;
151 bool logOnSuccess;
152 bool checkForLeaks;
153 OutputStyle output;
154 GrowableArray<std::pair<mach_port_t, _dyld_test_crash_handler_t>> crashHandlers;
155 };
156
157 // Okay, this is tricky. We need something with roughly he semantics of a weak def, but without using weak defs as their presence
158 // may impact certain tests. Instead we do the following:
159 //
160 // 1. Embed a stuct containing a lock and a pointer to our global state object in each binary
161 // 2. Once per binary we walk the entire image list looking for the first entry that also has state data
162 // 3. If it has state we lock its initializaion lock, and if it is not initialized we initialize it
163 // 4. We then copy the initalized pointer into our own state, and unlock the initializer lock
164 //
165 // This should work because the image list forms a stable ordering. The one loose end is if an executable is running where logging
166 // is only used in dylibs that are all being dlopned() and dlclosed. Since many dylibs cannot be dlclosed that should be a non-issue
167 // in practice.
168 };
169
170 __attribute__((section("__DATA,__dyld_test")))
171 static std::atomic<TestState*> sState;
172
173 kern_return_t
174 catch_mach_exception_raise(mach_port_t exception_port,
175 mach_port_t thread,
176 mach_port_t task,
177 exception_type_t exception,
178 mach_exception_data_t code,
179 mach_msg_type_number_t codeCnt)
180 {
181 _dyld_test_crash_handler_t crashHandler = NULL;
182 for (const auto& handler : TestState::getState()->getCrashHandlers()) {
183 if (handler.first == exception_port) {
184 crashHandler = handler.second;
185 }
186 }
187 if (crashHandler) {
188 if (exception == EXC_CORPSE_NOTIFY) {
189 crashHandler(task);
190 } else {
191 return KERN_FAILURE;
192 }
193 }
194 return KERN_SUCCESS;
195 }
196
197 kern_return_t
198 catch_mach_exception_raise_state(mach_port_t exception_port,
199 exception_type_t exception,
200 const mach_exception_data_t code,
201 mach_msg_type_number_t codeCnt,
202 int * flavor,
203 const thread_state_t old_state,
204 mach_msg_type_number_t old_stateCnt,
205 thread_state_t new_state,
206 mach_msg_type_number_t * new_stateCnt)
207 {
208 return KERN_NOT_SUPPORTED;
209 }
210
211 kern_return_t
212 catch_mach_exception_raise_state_identity(mach_port_t exception_port,
213 mach_port_t thread,
214 mach_port_t task,
215 exception_type_t exception,
216 mach_exception_data_t code,
217 mach_msg_type_number_t codeCnt,
218 int * flavor,
219 thread_state_t old_state,
220 mach_msg_type_number_t old_stateCnt,
221 thread_state_t new_state,
222 mach_msg_type_number_t * new_stateCnt)
223 {
224 return KERN_NOT_SUPPORTED;
225 }
226
227 _process::_process() : executablePath(nullptr), args(nullptr), env(nullptr), stdoutHandler(nullptr), stderrHandler(nullptr),
228 crashHandler(nullptr), exitHandler(nullptr), pid(0), arch(currentArch), suspended(false), async(false) {}
229 _process::~_process() {
230 if (stdoutHandler) { Block_release(stdoutHandler);}
231 if (stderrHandler) { Block_release(stderrHandler);}
232 if (crashHandler) { Block_release(crashHandler);}
233 if (exitHandler) { Block_release(exitHandler);}
234 }
235
236 void _process::set_executable_path(const char* EP) { executablePath = EP; }
237 void _process::set_args(const char** A) { args = A; }
238 void _process::set_env(const char** E) { env = E; }
239 void _process::set_stdout_handler(_dyld_test_reader_t SOH) { stdoutHandler = Block_copy(SOH); };
240 void _process::set_stderr_handler(_dyld_test_reader_t SEH) { stderrHandler = Block_copy(SEH); }
241 void _process::set_exit_handler(_dyld_test_exit_handler_t EH) { exitHandler = Block_copy(EH); }
242 void _process::set_crash_handler(_dyld_test_crash_handler_t CH) { crashHandler = Block_copy(CH); }
243 void _process::set_launch_suspended(bool S) { suspended = S; }
244 void _process::set_launch_async(bool S) { async = S; }
245 void _process::set_launch_arch(cpu_type_t A) { arch = A; }
246
247 pid_t _process::launch() {
248 dispatch_queue_t queue = dispatch_queue_create("com.apple.dyld.test.launch", NULL);
249 dispatch_block_t oneShotSemaphoreBlock = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{});
250 posix_spawn_file_actions_t fileActions = NULL;
251 posix_spawnattr_t attr = NULL;
252 dispatch_source_t stdoutSource = NULL;
253 dispatch_source_t stderrSource = NULL;
254 int stdoutPipe[2];
255 int stderrPipe[2];
256
257 if (posix_spawn_file_actions_init(&fileActions) != 0) {
258 FAIL("Setting up spawn filea actions");
259 }
260 if (posix_spawnattr_init(&attr) != 0) { FAIL("Setting up spawn attr"); }
261 if (posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0) {
262 FAIL("Setting up spawn attr: POSIX_SPAWN_START_SUSPENDED");
263 }
264
265 if (pipe(stdoutPipe) != 0) { FAIL("Setting up pipe"); }
266 if (posix_spawn_file_actions_addclose(&fileActions, stdoutPipe[0]) != 0) { FAIL("Setting up pipe"); }
267 if (posix_spawn_file_actions_adddup2(&fileActions, stdoutPipe[1], STDOUT_FILENO) != 0) { FAIL("Setting up pipe"); }
268 if (posix_spawn_file_actions_addclose(&fileActions, stdoutPipe[1]) != 0) { FAIL("Setting up pipe"); }
269 fcntl((int)stdoutPipe[0], F_SETFL, O_NONBLOCK);
270 stdoutSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)stdoutPipe[0], 0, queue);
271 dispatch_source_set_event_handler(stdoutSource, ^{
272 int fd = (int)dispatch_source_get_handle(stdoutSource);
273 if (stdoutHandler) {
274 stdoutHandler(fd);
275 } else {
276 char buffer[16384];
277 ssize_t size = 0;
278 do {
279 size = read(fd, &buffer[0], 16384);
280 } while (size > 0);
281 }
282 });
283 dispatch_source_set_cancel_handler(stdoutSource, ^{
284 dispatch_release(stdoutSource);
285 });
286 dispatch_resume(stdoutSource);
287
288 if (pipe(stderrPipe) != 0) { FAIL("Setting up pipe"); }
289 if (posix_spawn_file_actions_addclose(&fileActions, stderrPipe[0]) != 0) { FAIL("Setting up pipe"); }
290 if (posix_spawn_file_actions_adddup2(&fileActions, stderrPipe[1], STDERR_FILENO) != 0) { FAIL("Setting up pipe"); }
291 if (posix_spawn_file_actions_addclose(&fileActions, stderrPipe[1]) != 0) { FAIL("Setting up pipe"); }
292 fcntl((int)stderrPipe[0], F_SETFL, O_NONBLOCK);
293 stderrSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)stderrPipe[0], 0, queue);
294 dispatch_source_set_event_handler(stderrSource, ^{
295 int fd = (int)dispatch_source_get_handle(stderrSource);
296 if (stderrHandler) {
297 stderrHandler(fd);
298 } else {
299 char buffer[16384];
300 ssize_t size = 0;
301 do {
302 size = read(fd, &buffer[0], 16384);
303 } while (size > 0);
304 }
305 });
306 dispatch_source_set_cancel_handler(stderrSource, ^{
307 dispatch_release(stderrSource);
308 });
309 dispatch_resume(stderrSource);
310
311 if (crashHandler) {
312 auto& crashHandlers = TestState::getState()->getCrashHandlers();
313 mach_port_t exceptionPort = MACH_PORT_NULL;
314 mach_port_options_t options = { .flags = MPO_CONTEXT_AS_GUARD | MPO_STRICT | MPO_INSERT_SEND_RIGHT, .mpl = { 1 }};
315 if ( mach_port_construct(mach_task_self(), &options, (mach_port_context_t)exceptionPort, &exceptionPort) != KERN_SUCCESS ) {
316 FAIL("Could not construct port");
317 }
318 if (posix_spawnattr_setexceptionports_np(&attr, EXC_MASK_CRASH | EXC_MASK_CORPSE_NOTIFY, exceptionPort,
319 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0) != 0) {
320 FAIL("posix_spawnattr_setexceptionports_np failed");
321 }
322 crashHandlers.push_back(std::make_pair(exceptionPort, crashHandler));
323 dispatch_source_t crashSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, exceptionPort, 0, queue);
324 dispatch_source_set_event_handler(crashSource, ^{
325 dispatch_mig_server(crashSource, sizeof(union catch_mach_exc_request_reply), ::mach_exc_server);
326 });
327 dispatch_source_set_cancel_handler(crashSource, ^{
328 mach_port_destruct(mach_task_self(), exceptionPort, 0, (mach_port_context_t)exceptionPort);
329 });
330 dispatch_resume(crashSource);
331 }
332
333 pid_t pid;
334 uint32_t argc = 0;
335 if (args) {
336 for (argc = 0; args[argc] != NULL; ++argc) {}
337 }
338 ++argc;
339 const char *argv[argc+1];
340 argv[0] = executablePath;
341 for (uint32_t i = 1; i < argc; ++i) {
342 argv[i] = args[i-1];
343 }
344 argv[argc] = NULL;
345
346 int result = posix_spawn(&pid, executablePath, &fileActions, &attr, (char **)argv, (char **)env);
347 if ( result != 0 ) {
348 FAIL("posix_spawn(%s) failed, err=%d", executablePath, result);
349 }
350 dispatch_source_t exitSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (pid_t)pid,
351 DISPATCH_PROC_EXIT, queue);
352 dispatch_source_set_event_handler(exitSource, ^{
353 if (exitHandler) {
354 exitHandler((pid_t)dispatch_source_get_handle(exitSource));
355 }
356 dispatch_source_cancel(exitSource);
357 if (stdoutSource) {
358 dispatch_source_cancel(stdoutSource);
359 }
360 if (stderrSource) {
361 dispatch_source_cancel(stderrSource);
362 }
363 oneShotSemaphoreBlock();
364 dispatch_source_cancel(exitSource);
365 });
366 dispatch_resume(exitSource);
367
368 if (stdoutHandler) {
369 close(stdoutPipe[1]);
370 }
371 if (stderrHandler) {
372 close(stderrPipe[1]);
373 }
374 if (fileActions) {
375 posix_spawn_file_actions_destroy(&fileActions);
376 }
377 posix_spawnattr_destroy(&attr);
378 if (!suspended) {
379 kill(pid, SIGCONT);
380 }
381 if (!async) {
382 dispatch_block_wait(oneShotSemaphoreBlock, DISPATCH_TIME_FOREVER);
383 }
384 Block_release(oneShotSemaphoreBlock);
385 dispatch_release(queue);
386 return pid;
387 }
388
389 void *_process::operator new(size_t size) {
390 return malloc(size);
391 }
392
393 void _process::operator delete(void *ptr) {
394 free(ptr);
395 }
396
397 // MARK: Private implementation details
398
399 template<typename F>
400 static
401 void forEachEnvVar(const char* envp[], F&& f) {
402 for (uint32_t i = 0; envp[i] != nullptr; ++i) {
403 const char* envBegin = envp[i];
404 const char* envEnd = strchr(envp[i], '=');
405 if (!envEnd) { continue; }
406 size_t envSize = (envEnd-envBegin)+1;
407 const char* valBegin = envEnd+1;
408 const char* valEnd = strchr(envp[i], '\0');
409 if (!valEnd) { continue; }
410 size_t valSize = (valEnd-valBegin)+1;
411 char env[envSize];
412 char val[valSize];
413 strlcpy(&env[0], envBegin, envSize);
414 strlcpy(&val[0], valBegin, valSize);
415 f(&env[0], &val[0]);
416 }
417 }
418
419 uint8_t TestState::hexCharToUInt(const char hexByte, uint8_t* value) {
420 if (hexByte >= '0' && hexByte <= '9') {
421 *value = hexByte - '0';
422 return true;
423 } else if (hexByte >= 'A' && hexByte <= 'F') {
424 *value = hexByte - 'A' + 10;
425 return true;
426 } else if (hexByte >= 'a' && hexByte <= 'f') {
427 *value = hexByte - 'a' + 10;
428 return true;
429 }
430
431 return false;
432 }
433
434 uint64_t TestState::hexToUInt64(const char* startHexByte, const char** endHexByte) {
435 const char* scratch;
436 if (endHexByte == NULL) {
437 endHexByte = &scratch;
438 }
439 if (startHexByte == NULL)
440 return 0;
441 uint64_t retval = 0;
442 if (startHexByte[0] == '0' && startHexByte[1] == 'x') {
443 startHexByte +=2;
444 }
445 *endHexByte = startHexByte + 16;
446
447 //FIXME overrun?
448 for (uint32_t i = 0; i < 16; ++i) {
449 uint8_t value;
450 if (!hexCharToUInt(startHexByte[i], &value)) {
451 *endHexByte = &startHexByte[i];
452 break;
453 }
454 retval = (retval << 4) + value;
455 }
456 return retval;
457 }
458
459 void TestState::getLogsString(char** buffer)
460 {
461 char *logBuf = NULL;
462 if ( logs.count() ) {
463 size_t idx = 0;
464 size_t bufSize = 0;
465 for (const auto& log : logs) {
466 size_t logSize = strlen(log);
467 bufSize += logSize + 2; // \t and \n
468 logBuf = (char*)realloc(logBuf, bufSize);
469 strncpy(logBuf+idx, "\t", 1);
470 idx++;
471 strncpy(logBuf+idx, log, logSize);
472 idx += logSize;
473 strncpy(logBuf+idx, "\n", 1);
474 idx++;
475 }
476 logBuf = (char*)realloc(logBuf, bufSize + 1);
477 logBuf[bufSize] = '\0';
478 *buffer = logBuf;
479 }
480 }
481
482 TestState::TestState() : testName(__progname), logImmediate(false), logOnSuccess(false), checkForLeaks(false), output(Console) {
483 forEachEnvVar(environ, [this](const char* env, const char* val) {
484 if (strcmp(env, "TEST_LOG_IMMEDIATE") == 0) {
485 logImmediate = true;
486 }
487 if (strcmp(env, "TEST_LOG_ON_SUCCESS") == 0) {
488 logOnSuccess = true;
489 }
490 if (strcmp(env, "MallocStackLogging") == 0) {
491 checkForLeaks = true;
492 }
493 if (strcmp(env, "TEST_OUTPUT") == 0) {
494 if (strcmp(val, "BATS") == 0) {
495 output = BATS;
496 } else if (strcmp(val, "XCTest") == 0) {
497 output = XCTest;
498 }
499 }
500 });
501 if (output == BATS) {
502 printf("[BEGIN]");
503 if (checkForLeaks) {
504 printf(" MallocStackLogging=1 MallocDebugReport=none");
505 }
506 forEachEnvVar(environ, [this](const char* env, const char* val) {
507 if ((strncmp(env, "DYLD_", 5) == 0) || (strncmp(env, "TEST_", 5) == 0)) {
508 printf(" %s=%s", env, val);
509 }
510 });
511 printf(" %s", testName);
512 for (uint32_t i = 1; i < NXArgc; ++i) {
513 printf(" %s", NXArgv[i]);
514 }
515 printf("\n");
516 }
517 }
518
519 GrowableArray<std::pair<mach_port_t, _dyld_test_crash_handler_t>>& TestState::getCrashHandlers() {
520 return crashHandlers;
521 }
522
523 TestState* TestState::getState() {
524 if (!sState) {
525 uint32_t imageCnt = _dyld_image_count();
526 for (uint32_t i = 0; i < imageCnt; ++i) {
527 #if __LP64__
528 const struct mach_header_64* mh = (const struct mach_header_64*)_dyld_get_image_header(i);
529 #else
530 const struct mach_header* mh = _dyld_get_image_header(i);
531 #endif
532 if (mh->filetype != MH_EXECUTE) {
533 continue;
534 }
535 size_t size = 0;
536 auto state = (std::atomic<TestState*>*)getsectiondata(mh, "__DATA", "__dyld_test", &size);
537 // fprintf(stderr, "__dyld_test -> 0x%llx\n", state);
538 if (!state) {
539 fprintf(stderr, "Could not find test state in main executable TestState\n");
540 exit(0);
541 }
542 if (*state == nullptr) {
543 void *temp = malloc(sizeof(TestState));
544 auto newState = new (temp) TestState();
545 TestState* expected = nullptr;
546 if(!state->compare_exchange_strong(expected, newState)) {
547 newState->~TestState();
548 free(temp);
549 }
550 }
551 sState.store(*state);
552 }
553 }
554 assert(sState != nullptr);
555 return sState;
556 }
557
558 __attribute__((noreturn))
559 void TestState::runLeaks(void) {
560 auto testState = TestState::getState();
561 pid_t pid = getpid();
562 char pidString[32];
563 sprintf(&pidString[0], "%d", pid);
564 if (getuid() != 0) {
565 printf("Insufficient priviledges, skipping Leak check: %s\n", testState->testName);
566 exit(0);
567 }
568 const char *args[] = { pidString, NULL };
569 // We do this instead of using a dispatch_semaphore to prevent priority inversions
570 __block dispatch_data_t leaksOutput = NULL;
571 _process process;
572 process.set_executable_path("/usr/bin/leaks");
573 process.set_args(args);
574 process.set_stdout_handler(^(int fd) {
575 ssize_t size = 0;
576 do {
577 char buffer[16384];
578 size = read(fd, &buffer[0], 16384);
579 if (size == -1) { break; }
580 dispatch_data_t data = dispatch_data_create(&buffer[0], size, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
581 if (!leaksOutput) {
582 leaksOutput = data;
583 } else {
584 leaksOutput = dispatch_data_create_concat(leaksOutput, data);
585 }
586 } while (size > 0);
587 });
588 process.set_exit_handler(^(pid_t pid) {
589 int status = 0;
590 (void)waitpid(pid, &status, 0);
591
592 int exitStatus = WEXITSTATUS(status);
593 if (exitStatus == 0) {
594 PASS("No leaks");
595 } else {
596 if (leaksOutput) {
597 const void * buffer;
598 size_t size;
599 __unused dispatch_data_t map = dispatch_data_create_map(leaksOutput, &buffer, &size);
600 FAIL("Found Leaks:\n\n%s", buffer);
601 }
602 }
603 });
604
605 testState->checkForLeaks = false;
606 (void)process.launch();
607 exit(0);
608 }
609
610 void TestState::_PASSV(const char* file, unsigned line, const char* format, va_list args) {
611 if (output == None) {
612 exit(0);
613 }
614 if (checkForLeaks) {
615 runLeaks();
616 } else {
617 _IOlock.withLock([this,&format,&args,&file,&line](){
618 if (output == Console) {
619 printf("[\033[0;32mPASS\033[0m] %s: ", testName);
620 vprintf(format, args);
621 printf("\n");
622 if (logOnSuccess && logs.count()) {
623 printf("[\033[0;33mLOG\033[0m]\n");
624 for (const auto& log : logs) {
625 printf("\t%s\n", log);
626 }
627 }
628 } else if (output == BATS) {
629 printf("[PASS] %s: ", testName);
630 vprintf(format, args);
631 printf("\n");
632 if (logOnSuccess && logs.count()) {
633 printf("[LOG]\n");
634 for (const auto& log : logs) {
635 printf("\t%s\n", log);
636 }
637 }
638 } else if (output == XCTest) {
639 printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
640 printf("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
641 printf("<plist version=\"1.0\">");
642 printf("<dict>");
643 printf("<key>PASS</key><true />");
644 if (logOnSuccess) {
645 char *logBuffer = NULL;
646 getLogsString(&logBuffer);
647 if ( logBuffer != NULL ) {
648 printf("<key>LOGS</key><string>%s</string>", logBuffer);
649 free(logBuffer);
650 }
651 }
652 printf("</dict>");
653 printf("</plist>");
654 }
655 exit(0);
656 });
657 }
658 __builtin_unreachable();
659 }
660
661 void _PASS(const char* file, unsigned line, const char* format, ...) {
662 va_list args;
663 va_start (args, format);
664 TestState::getState()->_PASSV(file, line, format, args);
665 va_end (args);
666 }
667
668 void TestState::_FAILV(const char* file, unsigned line, const char* format, va_list args) {
669 if (output == None) {
670 exit(0);
671 }
672 _IOlock.withLock([this,&format,&args,&file,&line](){
673 if (output == Console) {
674 printf("[\033[0;31mFAIL\033[0m] %s: ", testName);
675 vprintf(format, args);
676 printf("\n");
677 printf("[\033[0;33mLOG\033[0m]\n");
678 if (logs.count()) {
679 for (const auto& log : logs) {
680 printf("\t%s\n", log);
681 }
682 }
683 } else if (output == BATS) {
684 printf("[FAIL] %s: ", testName);
685 vprintf(format, args);
686 printf("\n");
687 if (logs.count()) {
688 printf("[LOG]\n");
689 for (const auto& log : logs) {
690 printf("\t%s\n", log);
691 }
692 }
693 } else if (output == XCTest) {
694 printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
695 printf("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
696 printf("<plist version=\"1.0\">");
697 printf("<dict>");
698 printf("<key>PASS</key><false />");
699 printf("<key>FILE</key><string>%s</string>", file);
700 printf("<key>LINE</key><integer>%u</integer>", line);
701 char *buffer;
702 vasprintf(&buffer, format, args);
703 printf("<key>INFO</key><string>%s</string>", buffer);
704 free(buffer);
705 char *logBuffer = NULL;
706 getLogsString(&logBuffer);
707 if ( logBuffer != NULL ) {
708 printf("<key>LOGS</key><string>%s</string>", logBuffer);
709 free(logBuffer);
710 }
711 printf("</dict>");
712 printf("</plist>");
713 }
714 exit(0);
715 });
716 __builtin_unreachable();
717 }
718
719 void _FAIL(const char* file, unsigned line, const char* format, ...) {
720 va_list args;
721 va_start (args, format);
722 TestState::getState()->_FAILV(file, line, format, args);
723 va_end (args);
724 }
725
726 void TestState::_LOGV(const char* file, unsigned line, const char* format, va_list args) {
727 _IOlock.withLock([this,&format,&args](){
728 if (logImmediate) {
729 vprintf(format, args);
730 printf("\n");
731 } else {
732 char *str;
733 vasprintf(&str, format, args);
734 logs.push_back(str);
735 }
736 });
737 }
738
739 void _LOG(const char* file, unsigned line, const char* format, ...) {
740 va_list args;
741 va_start (args, format);
742 TestState::getState()->_LOGV(file, line, format, args);
743 va_end (args);
744 }
745
746 void _TIMEOUT(const char* file, unsigned line, uint64_t seconds) {
747 _LOG(file, line, "Registering %llu second test timeout", seconds);
748 dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, DISPATCH_TARGET_QUEUE_DEFAULT);
749 dispatch_time_t milestone = dispatch_time(DISPATCH_WALLTIME_NOW, seconds * NSEC_PER_SEC);
750 dispatch_source_set_timer(source, milestone, 0, 0);
751 dispatch_source_set_event_handler(source, ^{
752 FAIL("Test timed out");
753 });
754 dispatch_resume(source);
755 }