1 module covered; 2 3 version(unittest) import beep; 4 5 import core.stdc.stdio; 6 import core.stdc.stdlib : free, exit; 7 8 version(unittest) { } else extern(C) @nogc int main(int argc, char** argv) { 9 import core.stdc..string : strncpy, strcmp; 10 11 if(argc == 1) 12 argv[0].usage; 13 14 for(size_t i = 1; i < argc; ++i) { 15 if(!argv[i].strcmp("-h")) { 16 argv[0].usage; 17 } else if(!argv[i].strcmp("-a")) { 18 float total = 0.0; 19 size_t count; 20 21 if(argc - i == 1) 22 "At least 1 input file is expected".fail; 23 24 for(size_t j = i + 1; j < argc; ++j) { 25 auto file = fopen(argv[j], "r"); 26 if(file is null) 27 "Could not open %s".fail(argv[j]); 28 29 scope(exit) file.fclose; 30 31 auto r = file.getCoverage; 32 if(r != float.infinity) { 33 total += r; 34 ++count; 35 } 36 } 37 38 "Average: %.2f%%\n".printf(total / count); 39 40 break; 41 } else if(!argv[i].strcmp("-b")) { 42 if(argc - i < 3) 43 "At least 2 input files are expected".fail; 44 45 for(size_t j = i + 1; j < argc; ++j) { 46 auto file = fopen(argv[j], "r"); 47 if(file is null) 48 "Could not open %s".fail(argv[j]); 49 50 scope(exit) file.fclose; 51 52 auto coverage = file.getCoverage; 53 auto name = file.getSourceFile; 54 scope(exit) free(cast(void*) name); 55 56 coverage == float.infinity 57 ? "%40s has no code\n".printf(name) 58 : "%40s %.2f%%\n".printf(name, coverage); 59 } 60 break; 61 } else { 62 "Unknown argument '%s'\n".printf(argv[i]); 63 argv[0].usage; 64 } 65 } 66 67 return 0; 68 } 69 70 extern(C) @nogc void usage(char* program_name) { 71 printf("Usage: %s <options> FILE...\n", program_name); 72 73 exit(0); 74 } 75 76 extern(C) @nogc void fail(A...)(scope const(char)* fmt, A args) { 77 stderr.fprintf(fmt, args); 78 stderr.fprintf("\n"); 79 exit(-1); 80 } 81 82 extern(C) @nogc nothrow const(char)* getSourceFile(FILE* file) { 83 import core.stdc.stdlib : malloc; 84 import core.stdc..string : strrchr, strncpy; 85 86 char[1024] buffer; 87 88 file.fseek(0, SEEK_SET); 89 90 while(fgets(buffer.ptr, buffer.length, file) !is null) {} 91 92 auto end = buffer.ptr.strrchr('i'); // Line looks like `<filename> is <percent>% covered` 93 94 if(end is null) { // It's possible that file has no code 95 end = buffer.ptr.strrchr('h'); 96 97 if(end is null) 98 "Could not find source file name. Invalid file?".fail; 99 } 100 101 auto r = cast(char*) malloc(end - buffer.ptr - 1); 102 103 strncpy(r, buffer.ptr, end - buffer.ptr - 1); 104 105 return r; 106 } 107 108 @("getSourceFile()") 109 unittest { 110 import std..string : fromStringz; 111 { 112 auto file = fopen("test/hello.lst", "r"); 113 if(file is null) 114 throw new Exception("Could not open file"); 115 116 scope(exit) file.fclose; 117 118 auto p = file.getSourceFile; 119 scope(exit) free(cast(void*) p); 120 121 p.fromStringz.expect!equal("hello.d"); 122 } 123 124 { 125 auto file = fopen("test/empty.lst", "r"); 126 if(file is null) 127 throw new Exception("Could not open file"); 128 129 scope(exit) file.fclose; 130 131 auto p = file.getSourceFile; 132 scope(exit) free(cast(void*) p); 133 134 p.fromStringz.expect!equal("empty.d"); 135 } 136 } 137 138 extern(C) @nogc nothrow float getCoverage(FILE* file) { 139 import core.stdc.stdlib : atoi; 140 import core.stdc..string : strchr, strlen; 141 142 char[4096] buffer; 143 144 size_t covered; 145 size_t total; 146 147 file.fseek(0, SEEK_SET); 148 149 while(fgets(buffer.ptr, buffer.length, file) !is null) { 150 auto len = buffer.ptr.strlen; 151 if(len < 8 || buffer.ptr.strchr('|') is null) 152 continue; 153 154 if(buffer[0] == '0') { 155 ++total; 156 } else if(buffer.ptr.atoi != 0) { 157 ++total; 158 ++covered; 159 } 160 } 161 162 if(total == 0) 163 return float.infinity; 164 165 return cast(float) covered / cast(float) total * 100.0f; 166 } 167 168 @("getCoverage()") 169 unittest { 170 auto file = fopen("test/hello.lst", "r"); 171 if(file is null) 172 throw new Exception("Could not open file"); 173 174 scope(exit) file.fclose; 175 176 file.getCoverage.expect!equal(100.0f); 177 } 178 179 @("getCoverage() returns infinity on files that have no code") 180 unittest { 181 auto file = fopen("test/empty.lst", "r"); 182 if(file is null) 183 throw new Exception("Could not open file"); 184 185 scope(exit) file.fclose; 186 187 file.getCoverage.expect!equal(float.infinity); 188 }