root/log.d

Revision 1388:f22dcce89882, 25.2 kB (checked in by thomask, 5 years ago)

removed tracker code

Line 
1 module cn.kuehne.dstress.log;
2
3 private import std.string;
4 private import std.conv;
5 private import std.stdio;
6 private import std.stream;
7 private import std.file;
8 private import std.c.stdlib;
9 private import std.date;
10
11 static char[][] TORTURE_FLAGS = [
12     /* 0 args */
13     "",
14
15     /* 1 args */
16     "-g",
17     "-inline",
18     "-fPIC",
19     "-O",
20     "-release",
21
22     /* 2 args */
23     "-g -inline",
24     "-g -fPIC",
25     "-g -O",
26     "-g -release",
27     "-inline -fPIC",
28     "-inline -O",
29     "-inline -release",
30     "-fPIC -O",
31     "-fPIC -release",
32     "-O -release",
33
34     /* 3 args */
35     "-g -inline -fPIC",
36     "-g -inline -O",
37     "-g -inline -release",
38     "-g -fPIC -O",
39     "-g -fPIC -release",
40     "-g -O -release",
41     "-inline -fPIC -O",
42     "-inline -fPIC -release",
43     "-inline -O -release",
44     "-fPIC -O -release",
45
46     /* 4 args */
47     "-g -inline -fPIC -O",
48     "-g -inline -fPIC -release",
49     "-g -fPIC -O -release",
50     "-inline -fPIC -O -release",
51
52     /* 5 args */
53     "-g -inline -fPIC -O -release",
54
55     /* 4 args - ommitted */
56     "-g -inline -O -release"
57 ];
58
59 enum Result{
60     UNTESTED    = 0,
61     PASS        = 1 << 2,
62     XFAIL       = 2 << 2,
63     XPASS       = 3 << 2,
64     FAIL        = 4 << 2,
65     ERROR       = 5 << 2,
66     BASE_MASK   = 7 << 2,
67
68     EXT_MASK    = 3,
69     BAD_MSG     = 1,
70     BAD_GDB     = 2,
71    
72     MAX     = BAD_GDB + BASE_MASK
73 }
74
75 char[] toString(Result r){
76     switch(r & Result.BASE_MASK){
77         case Result.PASS: return "PASS";
78         case Result.XPASS: return "XPASS";
79         case Result.FAIL: return "FAIL";
80         case Result.XFAIL: return "XFAIL";
81         case Result.ERROR: return "ERROR";
82         case Result.UNTESTED: return "UNTESTED";
83         default:
84             break;
85     }
86     throw new Exception(format("unhandled Result value %s", cast(int)r));
87 }
88
89 char[] dateString(){
90     static char[] date;
91     if(date is null){
92         auto time = getUTCtime();
93         auto year = YearFromTime(time);
94         auto month = MonthFromTime(time);
95         auto day = DateFromTime(time);
96         date = format("%d-%02d-%02d", year, month+1, day);
97     }
98     return date;
99 }
100
101 char[][] unique(char[][] a){
102     char[][] b = a.sort;
103     char[][] back;
104
105     back ~= b[0];
106
107     size_t ii=0;
108     for(size_t i=0; i<b.length; i++){
109         if(back[ii]!=b[i]){
110             back~=b[i];
111             ii++;
112         }
113     }
114
115     return back;   
116 }
117
118 private{
119     version(Windows){
120         import std.c.windows.windows;
121         extern(Windows) BOOL GetFileTime(HANDLE hFile, LPFILETIME lpCreationTime, LPFILETIME lpLastAccessTime, LPFILETIME lpLastWriteTime);
122     }else version(linux){
123         import std.c.linux.linux;
124         version = Unix;
125     }else version(Unix){
126         import std.c.unix.unix;
127     }else{
128         static assert(0);
129     }
130
131     alias ulong FStime;
132
133     FStime getFStime(char[] fileName){
134         version(Windows){
135             HANDLE h;
136        
137             if (useWfuncs){
138                 wchar* namez = std.utf.toUTF16z(fileName);
139                 h = CreateFileW(namez,GENERIC_WRITE,0,null,OPEN_ALWAYS,
140                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null);
141             }else{
142                 char* namez = toMBSz(fileName);
143                 h = CreateFileA(namez,GENERIC_WRITE,0,null,OPEN_ALWAYS,
144                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null);
145             }
146
147             if (h == INVALID_HANDLE_VALUE)
148                 goto err;
149
150             FILETIME creationTime;
151             FILETIME accessTime;
152             FILETIME writeTime;
153        
154             BOOL b = GetFileTime(h, &creationTime, &accessTime, &writeTime);
155             if(b==1){
156                 long modA = writeTime.dwLowDateTime;
157                 long modB = writeTime.dwHighDateTime;
158                 return modA  | (modB << (writeTime.dwHighDateTime.sizeof*8));
159             }
160
161 err:
162             CloseHandle(h);
163             throw new Exception("failed to query file modification : "~fileName);
164         }else version(Unix){
165             char* namez = toStringz(fileName);
166             struct_stat statbuf;
167        
168             if(stat(namez, &statbuf)){
169                 throw new FileException(fileName, getErrno());
170             }
171
172             return statbuf.st_mtime;
173         }else{
174             static assert(0);
175         }
176     }
177 }
178
179 char[] cleanFileName(char[] file){
180     char[] back;
181     bool hadSep;
182
183     foreach(char c; file){
184         if(c == '/' || c == '\\'){
185             if(!hadSep){
186                 back ~= '/';
187                 hadSep = true;
188             }
189         }else{
190             back ~= c;
191             hadSep = false;
192         }
193     }
194
195     size_t start = 0;
196     while(back[start] <= ' ' && start < back.length){
197         start++;
198     }
199
200     size_t end = back.length-1;
201     while(back[end] <= ' ' && end >= start){
202         end--;
203     }
204
205     back = back[start .. end+1];
206
207     return back;
208 }
209
210 abstract class Test{
211     char[] name;
212     char[] file;
213
214     abstract Result condensed();
215
216     this(char[] file){
217         this.file = file;
218
219         int start = rfind(file, "/");
220         if(start<0){
221             start = 0;
222         }else{
223             start += 1;
224         }
225        
226         int end = rfind(file, ".");
227         if(end < start){
228             end = file.length;
229         }
230
231         name = file[start .. end];
232     }
233 }
234
235 class TortureTest : Test{
236     Result[] r;
237
238     Result condensed(){
239         Result back;
240         foreach(Result res; r){
241             if(res > back){
242                 back = res;
243             }
244         }
245
246         return back;
247     }
248
249     this(char[] file){
250         super(file);
251         r.length = TORTURE_FLAGS.length;
252     }
253
254     this(char[] file, Result[] result){
255         if(result.length != TORTURE_FLAGS.length){
256             throw new Exception(format("expected %s results but got %s (%s)", TORTURE_FLAGS.length, result.length, file));
257         }
258
259         super(file);
260         r = result.dup;
261     }
262 }
263
264 class Log{
265     TortureTest[char[]] torture;
266
267     char[] id;
268
269     this(char[] id){
270         this.id = id;
271     }
272
273     Regression[] findGlobalRegressions(Log[] logs){
274         Regression[] back;
275
276         if(logs.length < 1){
277             return back;
278         }
279
280         foreach(TortureTest currentTest; torture.values){
281             bool hadOldData = false;
282             Result oldResults[];
283             oldResults.length = TORTURE_FLAGS.length;
284             oldResults[] = Result.MAX;
285
286             foreach(Log l; logs){
287                 TortureTest* test = currentTest.file in l.torture;
288                 if(test !is null){
289                     hadOldData = true;
290                     foreach(size_t i, Result r; test.r){
291                         if(r != Result.UNTESTED && r < oldResults[i]){
292                             oldResults[i] = r;
293                         }
294                     }
295                 }
296             }
297
298             if(hadOldData){
299                 foreach(size_t i, Result r; oldResults){
300                     if((currentTest.r[i] != Result.UNTESTED) && (r != Result.MAX) && (currentTest.r[i] > r)){
301                         back ~= new Regression(currentTest.name, currentTest.file, r, currentTest.r[i], TORTURE_FLAGS[i]);
302                     }
303                 }
304             }
305         }
306         return back;
307     }
308
309     Regression[] findRegressions(Log oldLog){
310         Regression[] back;
311        
312         foreach(TortureTest t; torture.values){
313             TortureTest* oldT = t.file in oldLog.torture;
314
315             if(oldT !is null){
316                 foreach(size_t i, Result r; t.r){
317                     if(oldT.r[i] < r && oldT.r[i]){
318                         back ~= new Regression(t.name, t.file, oldT.r[i], r, TORTURE_FLAGS[i]);
319                     }
320                 }
321             }
322         }
323
324         return back;
325     }
326
327     char[][] genUpdateList(char[] testRoot){
328         char[][] updateList;
329         const char[][] statusList = ["compile", "nocompile", "run", "norun"];
330         char[] status;
331
332         void list(char[] path){
333             if(isdir(path)){
334                 foreach(char[] entry; listdir(path)){
335                     if(entry.length>0 && entry[0] != '.' && entry[0] != '~'){
336                         list(path~std.path.sep~entry);
337                     }
338                 }
339             }else if(isfile(path)){
340                 char[] file = path[testRoot.length + std.path.sep.length .. $];
341                 if(!(file in torture)){
342                     char[] output = "dstress torture-" ~ status ~ " " ~ file;
343                     updateList ~= output;
344                 }
345             }   
346         }
347        
348         foreach(char[] s; statusList){
349             status = s;
350             list(testRoot ~ std.path.sep ~ s);
351         }
352
353         return updateList;
354     }
355
356     void dropBogusResults(FStime recordTime, char[] testRoot){
357         uint totalCount = torture.length;
358        
359         char[][] sourcesTorture = torture.keys;
360         foreach(char[] source; sourcesTorture){
361             if(find(source, "complex/") < 0){
362                 try{
363                     FStime caseTime = getFStime(testRoot~std.path.sep~source);
364                     if(caseTime > recordTime){
365                         debug(drop) fwritefln(stderr, "dropped: %s", source);
366                         torture.remove(source);
367                     }
368                 }catch(Exception e){
369                     debug(drop) fwritefln(stderr, "dropped: %s", source);
370                     torture.remove(source);
371                 }
372             }
373             // asm-filter
374             int i = find(source, "asm_p");
375             if(i >= 0){
376                 torture.remove(source);
377             }
378         }
379         torture.rehash;
380        
381         writefln("dropped %s outdated tests (%s remaining)", totalCount - torture.length, torture.length);
382     }
383
384    
385     bool add(char[] line){
386         const char[] SUB = "Torture-Sub-";
387         const char[] TORTURE = "Torture:";
388
389         line = strip(line);
390         int id = -1;
391         Result r = Result.UNTESTED;
392
393         if(line.length > SUB.length && line[0 .. SUB.length] == SUB){
394             line = line[SUB.length .. $];
395             id = 0;
396             while(line[id] >= '0' && line[id] <= '9'){
397                 id++;
398             }
399             int start = id;
400             id = std.conv.toUint(line[0 .. id]);
401
402             while(line[start] != '-'){
403                 start++;
404             }
405             line = line[start+1 .. $];
406         }
407
408         char[][] token = split(line);
409         if(token.length < 2){
410             return false;
411         }
412         char[] file = strip(token[1]);
413
414         switch(token[0]){
415             case "PASS:":
416                 r = Result.PASS; break;
417             case "FAIL:":
418                 r = Result.FAIL; break;
419             case "XPASS:":
420                 r = Result.XPASS; break;
421             case "XFAIL:":
422                 r = Result.XFAIL; break;
423             case "ERROR:":
424                 r = Result.ERROR; break;
425             default:{
426                 if(token[0] == TORTURE){
427                     throw new Exception("not yet handled: "~line);
428                 }else if(id > -1){
429                     throw new Exception(format("bug in SUB line: (%s) %s", id, line));
430                 }
431             }
432         }
433
434         if(r != Result.UNTESTED){
435             if(std.string.find(line, "bad error message") > -1){
436                 r |= Result.BAD_MSG;   
437             }
438             if(std.string.find(line, "bad debugger message") > -1){
439                 r |= Result.BAD_MSG;   
440             }
441            
442             file = cleanFileName(file);
443            
444             if(id >= 0){
445                 // update sub
446                 id--;
447        
448                 TortureTest* test = file in torture;
449
450                 if(test is null){
451                     TortureTest t = new TortureTest(file);
452                     torture[file] = t;
453                     t.r[id] = r;
454                 }else{
455                     if(test.r[id] != Result.UNTESTED){
456                         test.r[] = Result.UNTESTED;
457                     }
458                     test.r[id] = r;
459                 }
460             }
461             return true;
462         }
463         return false;
464     }
465 }
466
467 class Regression{
468     Result before;
469     Result after;
470     char[] file;
471     char[] name;
472     char[] extInfo;
473
474     this(char[] name, char[] file, Result oldR, Result newR, char[] addonInfo=null){
475         this.file = file;
476         this.name = name;
477         before = oldR;
478         after = newR;
479         extInfo = addonInfo;
480     }
481
482     char[] toString(){
483         char[] back = .toString(before) ~" -> "~.toString(after)~": "~file;
484         if(extInfo.length > 0){
485             back ~= " ("~extInfo~")";
486         }
487         return back;
488     }
489 }
490
491 class Report{
492     char[] root;
493     Log[] log;
494     static const char[] header =
495         "<th>&#160;</th><th>-g</th><th>-inline</th>"
496         "<th>-fPIC</th><th>-O</th><th>-release</th>"
497         "<th>-g -inline</th><th>-g -fPIC</th><th>-g -O</th>"
498         "<th>-g -release</th><th>-inline -fPIC</th>"
499         "<th>-inline -O</th><th>-inline -release</th>"
500         "<th>-fPIC -O</th><th>-fPIC -release</th>"
501         "<th>-O -release</th><th>-g -inline -fPIC</th>"
502         "<th>-g -inline -O</th><th>-g -inline -release</th>"
503         "<th>-g -fPIC -O</th><th>-g -fPIC -release</th>"
504         "<th>-g -O -release</th><th>-inline -fPIC -O</th>"
505         "<th>-inline -fPIC -release</th><th>-inline -O -release</th>"
506         "<th>-fPIC -O -release</th><th>-g -inline -fPIC -O</th>"
507         "<th>-g -inline -fPIC -release</th>"
508         "<th>-g -fPIC -O -release</th>"
509         "<th>-inline -fPIC -O -release</th>"
510         "<th>-g -inline -fPIC -O -release</th>"
511         "<th>-g -inline -O -release</th>";
512    
513     this(char[] root, Log[] log){
514         this.root = root;
515         this.log = log;
516     }
517
518     void toHtml(OutputStream summary, OutputStream[] cases, bool[] hotspot){
519        
520         if(cases.length != log.length || cases.length != hotspot.length){
521             throw new Exception("unexpected argument length");
522         }
523         foreach(size_t i, Log l; log){
524             toHtml(l, cases[i]);
525         }
526         toHtmlSummary(summary, hotspot);
527     }
528
529     static char[] cleanFileName(char[] name){
530         int i = rfind(name, "_");
531         if(i > -1){
532             name = name[i+1 .. $];
533         }
534         i = rfind(name, ".");
535         if(i > -1){
536             name = name[0 .. i];
537         }
538         return name;
539     }
540    
541     static char[] streamLine(uint[] stats){
542         char[] buffer;
543         foreach(uint i; stats){
544             buffer ~= "<td>"~std.string.toString(i)~"</td>";
545         }
546         return buffer;
547     }
548
549     static void toHtml(Log log, OutputStream stream){
550         char[] cleanName = cleanFileName(log.id);
551         { // header
552             char[] name = toupper(cleanName);
553        
554             stream.writeLine("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>");
555             stream.writeLine("<html xmlns='http://www.w3.org/1999/xhtml' lang='en' xml:lang='en'>");
556
557             stream.writeLine("<head><title>DStress - Torture: "~name~"</title><link rel='stylesheet' type='text/css' href='formate.css' /><meta name='author' content='Thomas K&#252;hne' /><meta name='date' content='" ~ dateString() ~ "' /><link rel='shortcut icon' href='data:image/gif;base64,R0lGODlhEAAQAKEAALMfEuWoLv///7MfEiH5BAEKAAMALAAAAAAQABAAAAIpnI95wN06nARqyvSQM6Gz+g3dOGofiabqygYmNYbv/FKLld04aEG+UgAAOw==' type='image/gif' /></head>");
558             stream.writeLine("<body><center><h1>DStress - Torture: "~name~"</h1></center><center><small>by Thomas K&#252;hne &lt;<a href='mailto:thomas@kuehne.cn'>thomas@kuehne.cn</a>&gt;</small></center>");
559             stream.writeLine("<h2><a name='note' id='note'></a>Note</h2><blockquote>A detailed description of the testing and the used symbols can be found on the <a href='./dstress.html'>main page</a>.</blockquote>");
560         }
561        
562         { // stats
563             stream.writeLine("<h2><a name='summary' id='summary'></a>Summary</h2>");
564             uint[][] stats;
565             stats.length = 6;
566             foreach(inout uint[] array; stats){
567                 array.length = TORTURE_FLAGS.length;
568             }
569
570             foreach(TortureTest t; log.torture){
571                 foreach(int i, Result r; t.r){
572                     stats[r >> 2][i]++;
573                 }
574             }
575
576            
577             { // total
578                 uint total = 0;
579                
580                 foreach(uint[] cases; stats){
581                     total += cases[0];
582                 }
583
584                 uint config = 0;
585                 foreach(uint[] a; stats[1 .. $]){
586                     foreach(uint b; a){
587                         config += b;
588                     }
589                 }
590                
591                 stream.writeLine(format("<blockquote><dl><dt><strong>test cases:</strong></dt><dd>%d</dd><dt><strong>tested configurations:</strong></dt><dd>%d</dd></dl></blockquote>", total, config));
592             }
593
594
595             stream.writeLine("<table border='1' summary='nummeric summary of the test results'>");
596             stream.writeLine("\t<tr><td>&#160;</td>"~header~"</tr>");
597             stream.writeLine("\t<tr class='" ~ cast(char)('A'+Result.PASS)~"'><th>PASS</th>" ~ streamLine(stats[Result.PASS >> 2])~"</tr>");
598             stream.writeLine("\t<tr class='" ~ cast(char)('A'+Result.XFAIL)~"'><th>XFAIL</th>" ~ streamLine(stats[Result.XFAIL >> 2])~"</tr>");
599             stream.writeLine("\t<tr class='" ~ cast(char)('A'+Result.XPASS)~"'><th>XPASS</th>" ~ streamLine(stats[Result.XPASS >> 2])~"</tr>");
600             stream.writeLine("\t<tr class='" ~ cast(char)('A'+Result.FAIL)~"'><th>FAIL</th>" ~ streamLine(stats[Result.FAIL >> 2])~"</tr>");
601             stream.writeLine("\t<tr class='" ~ cast(char)('A'+Result.ERROR)~"'><th>ERROR</th>" ~ streamLine(stats[Result.ERROR >> 2])~"</tr>");
602             stream.writeLine("\t<tr class='" ~ cast(char)('A'+Result.UNTESTED)~"'><th>untested</th>" ~ streamLine(stats[Result.UNTESTED >> 2])~"</tr>");
603             stream.writeLine("</table>");
604         }
605
606         { // details
607             stream.writeLine("<h2><a name='details' id='details'></a>Details</h2>");
608             stream.writeLine("<table border='1' summary='detailed listing of all test cases with unexpected results'>");
609             stream.writeLine("<tr><td>&#160;</td>"~header~"</tr>");
610            
611             char[][] keys;
612             {
613                 char[][char[]] k;
614                 foreach(char[] org; log.torture.keys){
615                     char[] z = org;
616                     int i = rfind(z, "/");
617                     if(i > -1){
618                         z = z[i+1 .. $];
619                     }
620                     i = rfind(z, ".");
621                     if(i > -1){
622                         z = z[0 .. i];
623                     }
624                     if(z in k && find(org, "complex") < 0){
625                         throw new Exception("dublicate key "~org);
626                     }
627                     k[z] = org;
628                 }
629
630                 foreach(char[] x; k.keys.sort){
631                     keys ~= k[x];   
632                 }
633
634             }
635             foreach(char[] key; keys){
636                 TortureTest t = log.torture[key];
637                 Result plainR = t.condensed();
638
639                 if(plainR == Result.PASS
640                     || plainR == Result.XFAIL
641                     || plainR == Result.UNTESTED)
642                 {
643                     continue;
644                 }else{
645                     char[] name = replace(t.name, "_", " ");
646                     char[] src = t.file;
647                     if(find(src,"complex/") != -1){
648                         src = src[0 .. rfind(src, "/")];
649                     }
650                     char[] back = "<tr><th><a href=\"../"~src~"\" id='"~t.name~"'>"~name~"</a></th>";
651                     foreach(Result r; t.r){
652                         back ~= "<td class='" ~ cast(char)(r+'A') ~ "'>";
653                         if(r == Result.UNTESTED){
654                             back ~= "-";
655                         }else{
656                             try{
657                                 back ~= .toString(r & Result.BASE_MASK);
658                             }catch(Exception e){
659                                 throw new Exception(t.toString()~" ["~e.toString()~"]");
660                             }
661                         }
662                         back ~= "</td>";
663                     }
664                     stream.writeLine(back ~ "</tr>");
665                 }
666             }
667
668             stream.writeLine("<tr><td>&#160;</td>"~header~"</tr>");
669             stream.writeLine("</table>");
670         }
671        
672         { // footer
673             stream.writeLine("<div><br /><br /><hr /><a href='http://dstress.kuehne.cn/www/"~cleanName~".html'>http://dstress.kuehne.cn/www/"~cleanName~".html</a>&#160; &#160;" ~ dateString() ~ "</div>");
674             stream.writeLine("<center><a href='http://developer.berlios.de'><img src='http://developer.berlios.de/bslogo.php?group_id=2732' width='124' height='32' border='0' alt='BerliOS Logo' /></a></center>");
675
676             stream.writeLine("</body></html>");
677         }
678     }
679
680     void toHtmlSummary(OutputStream stream, bool[] hotspot){
681         if(hotspot.length != log.length){
682             throw new Exception("illegal hotspot length");
683         }
684
685         char[][] names;
686        
687         foreach(Log l; log){
688             names ~= l.torture.keys;
689         }
690
691         uint[][] stats;
692         stats.length = 6;
693         foreach(inout uint[] array; stats){
694             array.length = log.length;
695         }
696        
697
698         char[][char[]] keys;
699         {
700             foreach(char[] org; unique(names)){
701                 char[] z = org;
702                 int i = rfind(z, "/");
703                 if(i > -1){
704                     z = z[i+1 .. $];
705                 }
706                 i = rfind(z, ".");
707                 if(i > -1){
708                     z = z[0 .. i];
709                 }
710                 if(z in keys && -1 == find(org, "complex/")){
711                     throw new Exception("dublicate key "~org);
712                 }
713                 keys[z] = org;
714             }
715
716         }
717
718         { // total
719             uint total = keys.keys.length;
720             for(size_t i = 0; i < stats[0].length; i++){
721                 stats[0][i] = total;
722             }
723         }
724
725         char[][] badLines;
726         foreach(char[] name; keys.keys.sort){
727             char[] file = keys[name];
728             Result[] result = new Result[log.length];
729             bool isBadLine;
730             foreach(size_t i, Log l; log){
731                 auto t = file in l.torture;     
732
733                 if(t){
734                     Result r = t.condensed();
735                     result[i] = r;
736
737                     if(r != Result.UNTESTED){
738                         stats[result[i] >> 2][i]++;
739                         stats[0][i]--;
740                         if(hotspot[i] && r>= Result.XPASS){
741                             isBadLine = true;
742                         }
743                     }
744                 }
745
746             }
747            
748             if(isBadLine){
749                 char[] cleanName = replace(name, "_", " ");
750                 char[] back = "<tr><th><a href=\"../"~file~"\" id='"~name~"'>"~cleanName~"</a></th>";
751                 foreach(Result r; result){
752                     back ~= "<td class='" ~ cast(char)(r+'A') ~ "'>";
753                     if(r == Result.UNTESTED){
754                         back ~= "-";
755                     }else{
756                         try{
757                             back ~= .toString(r & Result.BASE_MASK);
758                         }catch(Exception e){
759                             throw new Exception("name:" ~name~" ["~e.toString()~"]");
760                         }
761                     }
762                     back ~= "</td>";
763                 }
764                 badLines ~= back ~ "</tr>";
765             }
766         }
767
768
769         // output
770         stream.writeLine("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>");
771         stream.writeLine("<html xmlns='http://www.w3.org/1999/xhtml'>");
772         stream.writeLine("<head><title>DStress Report</title><link rel='stylesheet' type='text/css' href='formate.css' /><meta name='author' content='Thomas K&#252;hne' /><meta name='date' content='"~dateString()~"' /></head>");
773         stream.writeLine("<body><center><h1>DStress Report</h1></center>");
774         stream.writeLine("<h2><a name='note' id='note'></a>Note</h2>");
775         stream.writeLine("<blockquote><p>A detailed description of the testing and the used symbols can be found on the <a href='./dstress.html'>main page</a>.</p></blockquote>");
776         stream.writeLine("<h2><a name='summary' id='summary'></a>Summary</h2>");
777
778         stream.writeLine("<table border='1' summary='nummeric summary of the test results'>");
779         char[] versionHeader = "<tr><td>&#160;</td>";
780         { // version header
781             foreach(Log l; log){
782                 char[] name = l.id;
783                 int i = rfind(name, "/");
784                 if(i > -1){
785                     name = name[i+1 .. $];
786                 }
787                 i = rfind(name, ".log");
788                 if(i + ".log".length == name.length){
789                     name = name[0 .. i];
790                 }
791                 versionHeader ~= "<th><a href='./" ~ cleanFileName(l.id) ~ ".html'>"~replace(name, "_", " ")~"</a></th>";
792             }
793             versionHeader ~= "</tr>";
794         }
795        
796         stream.writeLine("\t<tr><td>&#160;</td>"
797             "<th class='" ~ cast(char)('A'+Result.PASS)~"'>PASS</th>"
798             "<th class='" ~ cast(char)('A'+Result.XFAIL)~"'>XFAIL</th>"
799             "<th class='" ~ cast(char)('A'+Result.XPASS)~"'>XPASS</th>"
800             "<th class='" ~ cast(char)('A'+Result.FAIL)~"'>FAIL</th>"
801             "<th class='" ~ cast(char)('A'+Result.ERROR)~"'>ERROR</th>"
802             "<th class='" ~ cast(char)('A'+Result.UNTESTED)~"'>UNTESTED</th></tr>");
803         foreach(size_t j, Log l; log){
804             char[] row ="\t<tr>";
805             char[] name = l.id;
806             int i = rfind(name, "/");
807             if(i > -1){
808                 name = name[i+1 .. $];
809             }
810             i = rfind(name, ".log");
811             if(i + ".log".length == name.length){
812                 name = name[0 .. i];
813             }
814             row ~= "<th><a href='./" ~ cleanFileName(l.id) ~ ".html'>"~replace(name, "_", " ")~"</a></th>";
815             row ~= "<td class='" ~ cast(char)('A'+Result.PASS)~"'>" ~ std.string.toString(stats[Result.PASS >> 2][j]) ~"</td>";
816             row ~= "<td class='" ~ cast(char)('A'+Result.XFAIL)~"'>" ~ std.string.toString(stats[Result.XFAIL >> 2][j]) ~"</td>";
817             row ~= "<td class='" ~ cast(char)('A'+Result.XPASS)~"'>" ~ std.string.toString(stats[Result.XPASS >> 2][j]) ~"</td>";
818             row ~= "<td class='" ~ cast(char)('A'+Result.FAIL)~"'>" ~ std.string.toString(stats[Result.FAIL >> 2][j]) ~"</td>";
819             row ~= "<td class='" ~ cast(char)('A'+Result.ERROR)~"'>" ~ std.string.toString(stats[Result.ERROR >> 2][j]) ~"</td>";
820             row ~= "<td class='" ~ cast(char)('A'+Result.UNTESTED)~"'>" ~ std.string.toString(stats[Result.UNTESTED >> 2][j]) ~"</td>";
821             stream.writeLine(row ~ "</tr>");
822         }
823         stream.writeLine("</table>");
824
825         stream.writeLine("<h2><a name='details' id='details'></a>Details</h2>");
826         stream.writeLine("<table border='1'>");
827         stream.writeLine(versionHeader);
828         foreach(char[] line; badLines){
829             stream.writeLine(line);
830         }
831         stream.writeLine(versionHeader);
832         stream.writeLine("</table>");
833        
834         stream.writeLine("<div><br /><br /><hr /><a href='http://dstress.kuehne.cn/www/results.html'>http://dstress.kuehne.cn/www/results.html</a>&#160; &#160;"~dateString()~"</div>");
835
836         stream.writeLine("<!-- Start of StatCounter Code -->");
837         stream.writeLine("<script type='text/javascript'><!-- var sc_project=1337754; var sc_invisible=1; var sc_partition=12; var sc_security=\"a4a998fe\"; var sc_remove_link=1; //--> </script>");
838         stream.writeLine("<script type='text/javascript' src='http://www.statcounter.com/counter/counter_xhtml.js'></script><noscript><div class='statcounter'><img src='http://c13.statcounter.com/counter.php?sc_project=1337754&amp;amp;java=0&amp;amp;security=a4a998fe&amp;amp;invisible=1' class='statcounter' alt='counter' /></div></noscript>");
839         stream.writeLine("<!-- End of StatCounter Code -->");
840
841         stream.writeLine("</body></html>");
842     }
843 }
844
845 int main(char[][] args){
846
847     if(args.length < 4){
848         fwritefln(stderr, "%s <command> <root> <log.1> [<log.2> ...]", args[0]);
849         fwritefln(stderr, "known commands: genUpdateList findRegressions genReport");
850         return 1;
851     }
852    
853    
854     char[] command = args[1];
855     char[] root = args[2];
856     if(root.length < 1){
857         root = ".";
858     }
859     debug fwritefln(stderr, "command: %s", command);
860     debug fwritefln(stderr, "root: %s", root);
861
862     Report report = new Report(root, null);
863     bool[] hotspot;
864
865     switch(command){
866         case "genUpdateList", "findRegressions", "genReport": break;
867         default:{
868             fwritefln(stderr, "unknown command: %s", command);
869             return -1;
870         }
871     }
872
873     foreach(size_t id, char[] file; args[3 .. $]){
874         if(file.length > "--".length && file[0 .. 2] == "--"){
875             file = file[2..$];
876             hotspot ~= true;
877         }else{
878             hotspot ~= false;
879         }
880
881         writefln("parsing: %s", file);
882         FStime logTime  = getFStime(file);
883         debug fwritefln(stderr, "sourceTime: %s", logTime);
884
885         Log l= new Log(file);
886         Stream source = new BufferedFile(file, FileMode.In);
887         while(!source.eof()){
888             l.add(source.readLine());
889         }
890        
891         l.dropBogusResults(logTime, root);
892
893         report.log ~= l;
894     }
895
896     switch(command){
897         case "genUpdateList":{
898             foreach(Log l; report.log){
899                 char[][] update = l.genUpdateList(root);
900                 writefln("%s updates required (%s still up to date)", update.length, l.torture.length);
901
902                 char[] updateFile = l.id ~ ".update";
903                 try{std.file.remove(updateFile);}catch{}
904
905                 if(update.length){
906                     File f = new File(updateFile, FileMode.OutNew);
907                     foreach(char[] line; update){
908                         f.writeLine(line);
909                     }
910                     f.close();
911                 }
912             }
913             break;
914         }case "findRegressions":{
915             foreach(size_t i, Log newLog; report.log[1..$]){
916                 Regression[] newReg = newLog.findRegressions(report.log[i]);
917                 writefln("identified %s new regressions for %s", newReg.length, newLog.id);
918            
919                 Regression[] oldReg;
920                 {
921                     Regression[] oldRegT = newLog.findGlobalRegressions(report.log[0 .. i]);
922                     foreach(Regression a; oldRegT){
923                         foreach(Regression b; newReg){
924                             if(a.file == b.file && (!a.extInfo || a.extInfo == b.extInfo)){
925                                 goto handled;
926                             }
927                         }
928
929                         oldReg ~= a;
930                     handled: {}
931                     }
932                 }
933                
934                 writefln("identified %s old regressions for %s", oldReg.length, newLog.id);
935
936                 char[] regressionFile = newLog.id ~ ".regression";
937                 try{std.file.remove(regressionFile);}catch{}
938                
939                 if(newReg.length + oldReg.length){
940                     File f = new File(regressionFile, FileMode.OutNew);
941
942                     if(newReg.length){
943                         f.writeLine(format("%s new regressions", newReg.length));
944                         foreach(Regression r; newReg){
945                             f.writeLine(r.toString());
946                         }
947                     }
948
949                     if(oldReg.length){
950                         if(newReg.length){
951                             f.writeLine("");
952                         }
953                         f.writeLine(format("%s old regressions", oldReg.length));
954                         foreach(Regression r; oldReg){
955                             f.writeLine(r.toString());
956                         }
957                     }
958                    
959                     f.close();
960                 }
961             }
962             break; 
963         }case "genReport":{
964             OutputStream[] html;
965             OutputStream o = new File("./www/results.html", FileMode.OutNew);
966             foreach(Log l; report.log){
967                 html ~= new File("./www/"~Report.cleanFileName(l.id)~".html", FileMode.OutNew);
968             }
969
970             report.toHtml(o, html, hotspot);
971            
972             o.close();
973             foreach(OutputStream stream; html){
974                 stream.close();
975             }
976
977             break;
978         }default:{
979             fwritefln(stderr, "unknown command: %s", command);
980             return -1;
981         }
982     }
983    
984     return 0;
985 }
Note: See TracBrowser for help on using the browser.