805 lines · plain
1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5 6//7// testfilerunner.m8// testObjects9//10// Created by Blaine Garst on 9/24/08.11//12 13#import "testfilerunner.h"14#import <Foundation/Foundation.h>15#include <stdio.h>16#include <unistd.h>17#include <fcntl.h>18#include <string.h>19#include <stdlib.h>20#include <stdbool.h>21 22bool Everything = false; // do it also with 3 levels of optimization23bool DoClang = false;24 25static bool isDirectory(char *path);26static bool isExecutable(char *path);27static bool isYounger(char *source, char *binary);28static bool readErrorFile(char *buffer, const char *from);29 30__strong char *gcstrcpy2(__strong const char *arg, char *endp) {31 unsigned size = endp - arg + 1;32 __strong char *result = NSAllocateCollectable(size, 0);33 strncpy(result, arg, size);34 result[size-1] = 0;35 return result;36}37__strong char *gcstrcpy1(__strong char *arg) {38 unsigned size = strlen(arg) + 1;39 __strong char *result = NSAllocateCollectable(size, 0);40 strncpy(result, arg, size);41 result[size-1] = 0;42 return result;43}44 45@implementation TestFileExe46 47@synthesize options, compileLine, shouldFail, binaryName, sourceName;48@synthesize generator;49@synthesize libraryPath, frameworkPath;50 51- (NSString *)description {52 NSMutableString *result = [NSMutableString new];53 if (shouldFail) [result appendString:@"fail"];54 for (id x in compileLine) {55 [result appendString:[NSString stringWithFormat:@" %s", (char *)x]];56 }57 return result;58}59 60- (__strong char *)radar {61 return generator.radar;62}63 64- (bool) compileUnlessExists:(bool)skip {65 if (shouldFail) {66 printf("don't use this to compile anymore!\n");67 return false;68 }69 if (skip && isExecutable(binaryName) && !isYounger(sourceName, binaryName)) return true;70 int argc = [compileLine count];71 char *argv[argc+1];72 for (int i = 0; i < argc; ++i)73 argv[i] = (char *)[compileLine pointerAtIndex:i];74 argv[argc] = NULL;75 pid_t child = fork();76 if (child == 0) {77 execv(argv[0], argv);78 exit(10); // shouldn't happen79 }80 if (child < 0) {81 printf("fork failed\n");82 return false;83 }84 int status = 0;85 pid_t deadchild = wait(&status);86 if (deadchild != child) {87 printf("wait got %d instead of %d\n", deadchild, child);88 exit(1);89 }90 if (WEXITSTATUS(status) == 0) {91 return true;92 }93 printf("run failed\n");94 return false;95}96 97bool lookforIn(char *lookfor, const char *format, pid_t child) {98 char buffer[512];99 char got[512];100 sprintf(buffer, format, child); 101 bool gotOutput = readErrorFile(got, buffer);102 if (!gotOutput) {103 printf("**** didn't get an output file %s to analyze!!??\n", buffer);104 return false;105 }106 char *where = strstr(got, lookfor);107 if (!where) {108 printf("didn't find '%s' in output file %s\n", lookfor, buffer);109 return false;110 }111 unlink(buffer);112 return true;113}114 115- (bool) compileWithExpectedFailure {116 if (!shouldFail) {117 printf("Why am I being called?\n");118 return false;119 }120 int argc = [compileLine count];121 char *argv[argc+1];122 for (int i = 0; i < argc; ++i)123 argv[i] = (char *)[compileLine pointerAtIndex:i];124 argv[argc] = NULL;125 pid_t child = fork();126 char buffer[512];127 if (child == 0) {128 // in child129 sprintf(buffer, "/tmp/errorfile_%d", getpid());130 close(1);131 int fd = creat(buffer, 0777);132 if (fd != 1) {133 fprintf(stderr, "didn't open custom error file %s as 1, got %d\n", buffer, fd);134 exit(1);135 }136 close(2);137 dup(1);138 int result = execv(argv[0], argv);139 exit(10);140 }141 if (child < 0) {142 printf("fork failed\n");143 return false;144 }145 int status = 0;146 pid_t deadchild = wait(&status);147 if (deadchild != child) {148 printf("wait got %d instead of %d\n", deadchild, child);149 exit(11);150 }151 if (WIFEXITED(status)) {152 if (WEXITSTATUS(status) == 0) {153 return false;154 }155 }156 else {157 printf("***** compiler borked/ICEd/died unexpectedly (status %x)\n", status);158 return false;159 }160 char *error = generator.errorString;161 162 if (!error) return true;163#if 0164 char got[512];165 sprintf(buffer, "/tmp/errorfile_%d", child); 166 bool gotOutput = readErrorFile(got, buffer);167 if (!gotOutput) {168 printf("**** didn't get an error file %s to analyze!!??\n", buffer);169 return false;170 }171 char *where = strstr(got, error);172 if (!where) {173 printf("didn't find '%s' in error file %s\n", error, buffer);174 return false;175 }176 unlink(buffer);177#else178 if (!lookforIn(error, "/tmp/errorfile_%d", child)) return false;179#endif180 return true;181}182 183- (bool) run {184 if (shouldFail) return true;185 if (sizeof(long) == 4 && options & Do64) {186 return true; // skip 64-bit tests187 }188 int argc = 1;189 char *argv[argc+1];190 argv[0] = binaryName;191 argv[argc] = NULL;192 pid_t child = fork();193 if (child == 0) {194 // set up environment195 char lpath[1024];196 char fpath[1024];197 char *myenv[3];198 int counter = 0;199 if (libraryPath) {200 sprintf(lpath, "DYLD_LIBRARY_PATH=%s", libraryPath);201 myenv[counter++] = lpath;202 }203 if (frameworkPath) {204 sprintf(fpath, "DYLD_FRAMEWORK_PATH=%s", frameworkPath);205 myenv[counter++] = fpath;206 }207 myenv[counter] = NULL;208 if (generator.warningString) {209 // set up stdout/stderr210 char outfile[1024];211 sprintf(outfile, "/tmp/stdout_%d", getpid());212 close(2);213 close(1);214 creat(outfile, 0700);215 dup(1);216 }217 execve(argv[0], argv, myenv);218 exit(10); // shouldn't happen219 }220 if (child < 0) {221 printf("fork failed\n");222 return false;223 }224 int status = 0;225 pid_t deadchild = wait(&status);226 if (deadchild != child) {227 printf("wait got %d instead of %d\n", deadchild, child);228 exit(1);229 }230 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {231 if (generator.warningString) {232 if (!lookforIn(generator.warningString, "/tmp/stdout_%d", child)) return false;233 }234 return true;235 }236 printf("**** run failed for %s\n", binaryName);237 return false;238}239 240@end241 242@implementation TestFileExeGenerator243@synthesize filename, compilerPath, errorString;244@synthesize hasObjC, hasRR, hasGC, hasCPlusPlus, wantsC99, supposedToNotCompile, open, wants32, wants64;245@synthesize radar;246@synthesize warningString;247 248- (void)setFilename:(__strong char *)name {249 filename = gcstrcpy1(name);250}251- (void)setCompilerPath:(__strong char *)name {252 compilerPath = gcstrcpy1(name);253}254 255- (void)forMostThings:(NSMutableArray *)lines options:(int)options {256 TestFileExe *item = nil;257 item = [self lineForOptions:options];258 if (item) [lines addObject:item];259 item = [self lineForOptions:options|Do64];260 if (item) [lines addObject:item];261 item = [self lineForOptions:options|DoCPP];262 if (item) [lines addObject:item];263 item = [self lineForOptions:options|Do64|DoCPP];264 if (item) [lines addObject:item];265}266 267/*268 DoDashG = (1 << 8),269 DoDashO = (1 << 9),270 DoDashOs = (1 << 10),271 DoDashO2 = (1 << 11),272*/273 274- (void)forAllThings:(NSMutableArray *)lines options:(int)options {275 [self forMostThings:lines options:options];276 if (!Everything) {277 return;278 }279 // now do it with three explicit optimization flags280 [self forMostThings:lines options:options | DoDashO];281 [self forMostThings:lines options:options | DoDashOs];282 [self forMostThings:lines options:options | DoDashO2];283}284 285- (NSArray *)allLines {286 NSMutableArray *result = [NSMutableArray new];287 TestFileExe *item = nil;288 289 int options = 0;290 [self forAllThings:result options:0];291 [self forAllThings:result options:DoOBJC | DoRR];292 [self forAllThings:result options:DoOBJC | DoGC];293 [self forAllThings:result options:DoOBJC | DoGCRR];294 //[self forAllThings:result options:DoOBJC | DoRRGC];295 296 return result;297}298 299- (void)addLibrary:(const char *)dashLSomething {300 if (!extraLibraries) {301 extraLibraries = [NSPointerArray pointerArrayWithOptions:302 NSPointerFunctionsStrongMemory |303 NSPointerFunctionsCStringPersonality];304 }305 [extraLibraries addPointer:(void *)dashLSomething];306}307 308- (TestFileExe *)lineForOptions:(int)options { // nil if no can do309 if (hasObjC && !(options & DoOBJC)) return nil;310 if (hasCPlusPlus && !(options & DoCPP)) return nil;311 if (hasObjC) {312 if (!hasGC && (options & (DoGC|DoGCRR))) return nil; // not smart enough313 if (!hasRR && (options & (DoRR|DoRRGC))) return nil;314 }315 NSPointerArray *pa = [NSPointerArray pointerArrayWithOptions:316 NSPointerFunctionsStrongMemory |317 NSPointerFunctionsCStringPersonality];318 // construct path319 char path[512];320 path[0] = 0;321 if (!compilerPath) compilerPath = "/usr/bin";322 if (compilerPath) {323 strcat(path, compilerPath);324 strcat(path, "/");325 }326 if (options & DoCPP) {327 strcat(path, DoClang ? "clang++" : "g++-4.2");328 }329 else {330 strcat(path, DoClang ? "clang" : "gcc-4.2");331 }332 [pa addPointer:gcstrcpy1(path)];333 if (options & DoOBJC) {334 if (options & DoCPP) {335 [pa addPointer:"-ObjC++"];336 }337 else {338 [pa addPointer:"-ObjC"];339 }340 }341 [pa addPointer:"-g"];342 if (options & DoDashO) [pa addPointer:"-O"];343 else if (options & DoDashO2) [pa addPointer:"-O2"];344 else if (options & DoDashOs) [pa addPointer:"-Os"];345 if (wantsC99 && (! (options & DoCPP))) {346 [pa addPointer:"-std=c99"];347 [pa addPointer:"-fblocks"];348 }349 [pa addPointer:"-arch"];350 [pa addPointer: (options & Do64) ? "x86_64" : "i386"];351 352 if (options & DoOBJC) {353 switch (options & (DoRR|DoGC|DoGCRR|DoRRGC)) {354 case DoRR:355 break;356 case DoGC:357 [pa addPointer:"-fobjc-gc-only"];358 break;359 case DoGCRR:360 [pa addPointer:"-fobjc-gc"];361 break;362 case DoRRGC:363 printf("DoRRGC unsupported right now\n");364 [pa addPointer:"-c"];365 return nil;366 }367 [pa addPointer:"-framework"];368 [pa addPointer:"Foundation"];369 }370 [pa addPointer:gcstrcpy1(filename)];371 [pa addPointer:"-o"];372 373 path[0] = 0;374 strcat(path, filename);375 strcat(path, ".");376 strcat(path, (options & Do64) ? "64" : "32");377 if (options & DoOBJC) {378 switch (options & (DoRR|DoGC|DoGCRR|DoRRGC)) {379 case DoRR: strcat(path, "-rr"); break;380 case DoGC: strcat(path, "-gconly"); break;381 case DoGCRR: strcat(path, "-gcrr"); break;382 case DoRRGC: strcat(path, "-rrgc"); break;383 }384 }385 if (options & DoCPP) strcat(path, "++");386 if (options & DoDashO) strcat(path, "-O");387 else if (options & DoDashO2) strcat(path, "-O2");388 else if (options & DoDashOs) strcat(path, "-Os");389 if (wantsC99) strcat(path, "-C99");390 strcat(path, DoClang ? "-clang" : "-gcc");391 strcat(path, "-bin");392 TestFileExe *result = [TestFileExe new];393 result.binaryName = gcstrcpy1(path); // could snarf copy in pa394 [pa addPointer:result.binaryName];395 for (id cString in extraLibraries) {396 [pa addPointer:cString];397 }398 399 result.sourceName = gcstrcpy1(filename); // could snarf copy in pa400 result.compileLine = pa;401 result.options = options;402 result.shouldFail = supposedToNotCompile;403 result.generator = self;404 return result;405}406 407+ (NSArray *)generatorsFromPath:(NSString *)path {408 FILE *fp = fopen([path fileSystemRepresentation], "r");409 if (fp == NULL) return nil;410 NSArray *result = [self generatorsFromFILE:fp];411 fclose(fp);412 return result;413}414 415#define LOOKFOR "CON" "FIG"416 417char *__strong parseRadar(char *line) {418 line = strstr(line, "rdar:"); // returns beginning419 char *endp = line + strlen("rdar:");420 while (*endp && *endp != ' ' && *endp != '\n')421 ++endp;422 return gcstrcpy2(line, endp);423}424 425- (void)parseLibraries:(const char *)line {426 start:427 line = strstr(line, "-l");428 char *endp = (char *)line + 2;429 while (*endp && *endp != ' ' && *endp != '\n')430 ++endp;431 [self addLibrary:gcstrcpy2(line, endp)];432 if (strstr(endp, "-l")) {433 line = endp;434 goto start;435 }436}437 438+ (TestFileExeGenerator *)generatorFromLine:(char *)line filename:(char *)filename {439 TestFileExeGenerator *item = [TestFileExeGenerator new];440 item.filename = gcstrcpy1(filename);441 if (strstr(line, "GC")) item.hasGC = true;442 if (strstr(line, "RR")) item.hasRR = true;443 if (strstr(line, "C++")) item.hasCPlusPlus = true;444 if (strstr(line, "-C99")) {445 item.wantsC99 = true;446 }447 if (strstr(line, "64")) item.wants64 = true;448 if (strstr(line, "32")) item.wants32 = true;449 if (strstr(line, "-l")) [item parseLibraries:line];450 if (strstr(line, "open")) item.open = true;451 if (strstr(line, "FAIL")) item.supposedToNotCompile = true; // old452 // compile time error453 if (strstr(line, "error:")) {454 item.supposedToNotCompile = true;455 // zap newline456 char *error = strstr(line, "error:") + strlen("error:");457 // make sure we have something before the newline458 char *newline = strstr(error, "\n");459 if (newline && ((newline-error) > 1)) {460 *newline = 0;461 item.errorString = gcstrcpy1(strstr(line, "error:") + strlen("error: "));462 }463 }464 // run time warning465 if (strstr(line, "runtime:")) {466 // zap newline467 char *error = strstr(line, "runtime:") + strlen("runtime:");468 // make sure we have something before the newline469 char *newline = strstr(error, "\n");470 if (newline && ((newline-error) > 1)) {471 *newline = 0;472 item.warningString = gcstrcpy1(strstr(line, "runtime:") + strlen("runtime:"));473 }474 }475 if (strstr(line, "rdar:")) item.radar = parseRadar(line);476 if (item.hasGC || item.hasRR) item.hasObjC = true;477 if (!item.wants32 && !item.wants64) { // give them both if they ask for neither478 item.wants32 = item.wants64 = true;479 }480 return item;481}482 483+ (NSArray *)generatorsFromFILE:(FILE *)fp {484 NSMutableArray *result = [NSMutableArray new];485 // pretend this is a grep LOOKFOR *.[cmCM][cmCM] input486 // look for487 // filename: ... LOOKFOR [GC] [RR] [C++] [FAIL ...]488 char buf[512];489 while (fgets(buf, 512, fp)) {490 char *config = strstr(buf, LOOKFOR);491 if (!config) continue;492 char *filename = buf;493 char *end = strchr(buf, ':');494 *end = 0;495 [result addObject:[self generatorFromLine:config filename:filename]];496 }497 return result;498}499 500+ (TestFileExeGenerator *)generatorFromFilename:(char *)filename {501 FILE *fp = fopen(filename, "r");502 if (!fp) {503 printf("didn't open %s!!\n", filename);504 return nil;505 }506 char buf[512];507 while (fgets(buf, 512, fp)) {508 char *config = strstr(buf, LOOKFOR);509 if (!config) continue;510 fclose(fp);511 return [self generatorFromLine:config filename:filename];512 }513 fclose(fp);514 // guess from filename515 char *ext = strrchr(filename, '.');516 if (!ext) return nil;517 TestFileExeGenerator *result = [TestFileExeGenerator new];518 result.filename = gcstrcpy1(filename);519 if (!strncmp(ext, ".m", 2)) {520 result.hasObjC = true;521 result.hasRR = true;522 result.hasGC = true;523 }524 else if (!strcmp(ext, ".c")) {525 ;526 }527 else if (!strcmp(ext, ".M") || !strcmp(ext, ".mm")) {528 result.hasObjC = true;529 result.hasRR = true;530 result.hasGC = true;531 result.hasCPlusPlus = true;532 }533 else if (!strcmp(ext, ".cc")534 || !strcmp(ext, ".cp")535 || !strcmp(ext, ".cxx")536 || !strcmp(ext, ".cpp")537 || !strcmp(ext, ".CPP")538 || !strcmp(ext, ".c++")539 || !strcmp(ext, ".C")) {540 result.hasCPlusPlus = true;541 }542 else {543 printf("unknown extension, file %s ignored\n", filename);544 result = nil;545 }546 return result;547 548}549 550- (NSString *)description {551 return [NSString stringWithFormat:@"%s: %s%s%s%s%s%s",552 filename,553 LOOKFOR,554 hasGC ? " GC" : "",555 hasRR ? " RR" : "",556 hasCPlusPlus ? " C++" : "",557 wantsC99 ? "C99" : "",558 supposedToNotCompile ? " FAIL" : ""];559}560 561@end562 563void printDetails(NSArray *failures, const char *whatAreThey) {564 if ([failures count]) {565 NSMutableString *output = [NSMutableString new];566 printf("%s:\n", whatAreThey);567 for (TestFileExe *line in failures) {568 printf("%s", line.binaryName);569 char *radar = line.generator.radar;570 if (radar)571 printf(" (due to %s?),", radar);572 printf(" recompile via:\n%s\n\n", line.description.UTF8String);573 }574 printf("\n");575 }576}577 578void help(const char *whoami) {579 printf("Usage: %s [-fast] [-e] [-dyld librarypath] [gcc4.2dir] [-- | source1 ...]\n", whoami);580 printf(" -fast don't recompile if binary younger than source\n");581 printf(" -open only run tests that are thought to still be unresolved\n");582 printf(" -clang use the clang and clang++ compilers\n");583 printf(" -e compile all variations also with -Os, -O2, -O3\n");584 printf(" -dyld p override DYLD_LIBRARY_PATH and DYLD_FRAMEWORK_PATH to p when running tests\n");585 printf(" <compilerpath> directory containing gcc-4.2 (or clang) that you wish to use to compile the tests\n");586 printf(" -- assume stdin is a grep CON" "FIG across the test sources\n");587 printf(" otherwise treat each remaining argument as a single test file source\n");588 printf("%s will compile and run individual test files under a variety of compilers, c, obj-c, c++, and objc++\n", whoami);589 printf(" .c files are compiled with all four compilers\n");590 printf(" .m files are compiled with objc and objc++ compilers\n");591 printf(" .C files are compiled with c++ and objc++ compilers\n");592 printf(" .M files are compiled only with the objc++ compiler\n");593 printf("(actually all forms of extensions recognized by the compilers are honored, .cc, .c++ etc.)\n");594 printf("\nTest files should run to completion with no output and exit (return) 0 on success.\n");595 printf("Further they should be able to be compiled and run with GC on or off and by the C++ compilers\n");596 printf("A line containing the string CON" "FIG within the source enables restrictions to the above assumptions\n");597 printf("and other options.\n");598 printf("Following CON" "FIG the string\n");599 printf(" C++ restricts the test to only be run by c++ and objc++ compilers\n");600 printf(" GC restricts the test to only be compiled and run with GC on\n");601 printf(" RR (retain/release) restricts the test to only be compiled and run with GC off\n");602 printf("Additionally,\n");603 printf(" -C99 restricts the C versions of the test to -fstd=c99 -fblocks\n");604 printf(" -O adds the -O optimization level\n");605 printf(" -O2 adds the -O2 optimization level\n");606 printf(" -Os adds the -Os optimization level\n");607 printf("Files that are known to exhibit unresolved problems can provide the term \"open\" and this can");608 printf("in turn allow highlighting of fixes that have regressed as well as identify that fixes are now available.\n");609 printf("Files that exhibit known bugs may provide\n");610 printf(" rdar://whatever such that if they fail the rdar will get cited\n");611 printf("Files that are expected to fail to compile should provide, as their last token sequence,\n");612 printf(" error:\n");613 printf(" or error: substring to match.\n");614 printf("Files that are expected to produce a runtime error message should provide, as their last token sequence,\n");615 printf(" warning: string to match\n");616 printf("\n%s will compile and run all configurations of the test files and report a summary at the end. Good luck.\n", whoami);617 printf(" Blaine Garst blaine@apple.com\n");618}619 620int main(int argc, char *argv[]) {621 printf("running on %s-bit architecture\n", sizeof(long) == 4 ? "32" : "64");622 char *compilerDir = "/usr/bin";623 NSMutableArray *generators = [NSMutableArray new];624 bool doFast = false;625 bool doStdin = false;626 bool onlyOpen = false;627 char *libraryPath = getenv("DYLD_LIBRARY_PATH");628 char *frameworkPath = getenv("DYLD_FRAMEWORK_PATH");629 // process options630 while (argc > 1) {631 if (!strcmp(argv[1], "-fast")) {632 doFast = true;633 --argc;634 ++argv;635 }636 else if (!strcmp(argv[1], "-dyld")) {637 doFast = true;638 --argc;639 ++argv;640 frameworkPath = argv[1];641 libraryPath = argv[1];642 --argc;643 ++argv;644 }645 else if (!strcmp(argv[1], "-open")) {646 onlyOpen = true;647 --argc;648 ++argv;649 }650 else if (!strcmp(argv[1], "-clang")) {651 DoClang = true;652 --argc;653 ++argv;654 }655 else if (!strcmp(argv[1], "-e")) {656 Everything = true;657 --argc;658 ++argv;659 }660 else if (!strcmp(argv[1], "--")) {661 doStdin = true;662 --argc;663 ++argv;664 }665 else if (!strcmp(argv[1], "-")) {666 help(argv[0]);667 return 1;668 }669 else if (argc > 1 && isDirectory(argv[1])) {670 compilerDir = argv[1];671 ++argv;672 --argc;673 }674 else675 break;676 }677 // process remaining arguments, or stdin678 if (argc == 1) {679 if (doStdin)680 generators = (NSMutableArray *)[TestFileExeGenerator generatorsFromFILE:stdin];681 else {682 help(argv[0]);683 return 1;684 }685 }686 else while (argc > 1) {687 TestFileExeGenerator *generator = [TestFileExeGenerator generatorFromFilename:argv[1]];688 if (generator) [generators addObject:generator];689 ++argv;690 --argc;691 }692 // see if we can generate all possibilities693 NSMutableArray *failureToCompile = [NSMutableArray new];694 NSMutableArray *failureToFailToCompile = [NSMutableArray new];695 NSMutableArray *failureToRun = [NSMutableArray new];696 NSMutableArray *successes = [NSMutableArray new];697 for (TestFileExeGenerator *generator in generators) {698 //NSLog(@"got %@", generator);699 if (onlyOpen && !generator.open) {700 //printf("skipping resolved test %s\n", generator.filename);701 continue; // skip closed if onlyOpen702 }703 if (!onlyOpen && generator.open) {704 //printf("skipping open test %s\n", generator.filename);705 continue; // skip open if not asked for onlyOpen706 }707 generator.compilerPath = compilerDir;708 NSArray *tests = [generator allLines];709 for (TestFileExe *line in tests) {710 line.frameworkPath = frameworkPath; // tell generators about it instead XXX711 line.libraryPath = libraryPath; // tell generators about it instead XXX712 if ([line shouldFail]) {713 if (doFast) continue; // don't recompile & don't count as success714 if ([line compileWithExpectedFailure]) {715 [successes addObject:line];716 }717 else718 [failureToFailToCompile addObject:line];719 }720 else if ([line compileUnlessExists:doFast]) {721 if ([line run]) {722 printf("%s ran successfully\n", line.binaryName);723 [successes addObject:line];724 }725 else {726 [failureToRun addObject:line];727 }728 }729 else {730 [failureToCompile addObject:line];731 }732 }733 }734 printf("\n--- results ---\n\n%lu successes\n%lu unexpected compile failures\n%lu failure to fail to compile errors\n%lu run failures\n",735 [successes count], [failureToCompile count], [failureToFailToCompile count], [failureToRun count]);736 printDetails(failureToCompile, "unexpected compile failures");737 printDetails(failureToFailToCompile, "should have failed to compile but didn't failures");738 printDetails(failureToRun, "run failures");739 740 if (onlyOpen && [successes count]) {741 NSMutableSet *radars = [NSMutableSet new];742 printf("The following tests ran successfully suggesting that they are now resolved:\n");743 for (TestFileExe *line in successes) {744 printf("%s\n", line.binaryName);745 if (line.radar) [radars addObject:line.generator];746 }747 if ([radars count]) {748 printf("The following radars may be resolved:\n");749 for (TestFileExeGenerator *line in radars) {750 printf("%s\n", line.radar);751 }752 }753 }754 755 return [failureToCompile count] + [failureToRun count];756}757 758#include <sys/stat.h>759 760static bool isDirectory(char *path) {761 struct stat statb;762 int retval = stat(path, &statb);763 if (retval != 0) return false;764 if (statb.st_mode & S_IFDIR) return true;765 return false;766}767 768static bool isExecutable(char *path) {769 struct stat statb;770 int retval = stat(path, &statb);771 if (retval != 0) return false;772 if (!(statb.st_mode & S_IFREG)) return false;773 if (statb.st_mode & S_IXUSR) return true;774 return false;775}776 777static bool isYounger(char *source, char *binary) {778 struct stat statb;779 int retval = stat(binary, &statb);780 if (retval != 0) return true; // if doesn't exit, lie781 782 struct stat stata;783 retval = stat(source, &stata);784 if (retval != 0) return true; // we're hosed785 // the greater the timeval the younger it is786 if (stata.st_mtimespec.tv_sec > statb.st_mtimespec.tv_sec) return true;787 if (stata.st_mtimespec.tv_nsec > statb.st_mtimespec.tv_nsec) return true;788 return false;789}790 791static bool readErrorFile(char *buffer, const char *from) {792 int fd = open(from, 0);793 if (fd < 0) {794 printf("didn't open %s, (might not have been created?)\n", buffer);795 return false;796 }797 int count = read(fd, buffer, 512);798 if (count < 1) {799 printf("read error on %s\n", buffer);800 return false;801 }802 buffer[count-1] = 0; // zap newline803 return true;804}805