Mercurial > projects > ldc
annotate tests/makewebstatistics.d @ 1351:8d501abecd24
Initial (but disabled) fix for ticket #294 , the actual part that fixes the bug is in a #if 0 block as I'm afraid it will cause regressions. I'm most likely not going to be around tonight, and maybe not tomorrow as well, so I'm pushing it in case someone wants to run some serious testing/investigate the problem noted in llvmhelpers.cpp : realignOffset .
author | Tomas Lindquist Olsen <tomas.l.olsen gmail com> |
---|---|
date | Thu, 14 May 2009 17:20:17 +0200 |
parents | 3e98925bcc39 |
children |
rev | line source |
---|---|
255 | 1 // Based on DSTRESS code by Thomas Kühne |
2 | |
3 module findregressions; | |
4 | |
5 private import std.string; | |
6 private import std.conv; | |
7 private import std.stdio; | |
8 private import std.stream; | |
9 private import std.file; | |
10 private import std.c.stdlib; | |
11 private import std.date; | |
12 private import std.path; | |
13 | |
14 | |
15 enum Result{ | |
16 UNTESTED = 0, | |
17 PASS = 1 << 2, | |
18 XFAIL = 2 << 2, | |
19 XPASS = 3 << 2, | |
20 FAIL = 4 << 2, | |
21 ERROR = 5 << 2, | |
22 BASE_MASK = 7 << 2, | |
23 | |
24 EXT_MASK = 3, | |
25 BAD_MSG = 1, | |
26 BAD_GDB = 2, | |
27 | |
28 MAX = BAD_GDB + BASE_MASK | |
29 } | |
30 | |
31 char[] toString(Result r){ | |
32 switch(r & Result.BASE_MASK){ | |
33 case Result.PASS: return "PASS"; | |
34 case Result.XPASS: return "XPASS"; | |
35 case Result.FAIL: return "FAIL"; | |
36 case Result.XFAIL: return "XFAIL"; | |
37 case Result.ERROR: return "ERROR"; | |
38 case Result.UNTESTED: return "UNTESTED"; | |
39 default: | |
40 break; | |
41 } | |
42 throw new Exception(format("unhandled Result value %s", cast(int)r)); | |
43 } | |
44 | |
45 char[] dateString(){ | |
46 static char[] date; | |
47 if(date is null){ | |
48 auto time = getUTCtime(); | |
49 auto year = YearFromTime(time); | |
50 auto month = MonthFromTime(time); | |
51 auto day = DateFromTime(time); | |
52 date = format("%d-%02d-%02d", year, month+1, day); | |
53 } | |
54 return date; | |
55 } | |
56 | |
57 char[][] unique(char[][] a){ | |
58 char[][] b = a.sort; | |
59 char[][] back; | |
60 | |
61 back ~= b[0]; | |
62 | |
63 size_t ii=0; | |
64 for(size_t i=0; i<b.length; i++){ | |
65 if(back[ii]!=b[i]){ | |
66 back~=b[i]; | |
67 ii++; | |
68 } | |
69 } | |
70 | |
71 return back; | |
72 } | |
73 | |
74 private{ | |
75 version(Windows){ | |
76 import std.c.windows.windows; | |
77 extern(Windows) BOOL GetFileTime(HANDLE hFile, LPFILETIME lpCreationTime, LPFILETIME lpLastAccessTime, LPFILETIME lpLastWriteTime); | |
78 }else version(linux){ | |
79 import std.c.linux.linux; | |
80 version = Unix; | |
81 }else version(Unix){ | |
82 import std.c.unix.unix; | |
83 }else{ | |
84 static assert(0); | |
85 } | |
86 | |
87 alias ulong FStime; | |
88 | |
89 FStime getFStime(char[] fileName){ | |
90 version(Windows){ | |
91 HANDLE h; | |
92 | |
93 if (useWfuncs){ | |
94 wchar* namez = std.utf.toUTF16z(fileName); | |
95 h = CreateFileW(namez,GENERIC_WRITE,0,null,OPEN_ALWAYS, | |
96 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null); | |
97 }else{ | |
98 char* namez = toMBSz(fileName); | |
99 h = CreateFileA(namez,GENERIC_WRITE,0,null,OPEN_ALWAYS, | |
100 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null); | |
101 } | |
102 | |
103 if (h == INVALID_HANDLE_VALUE) | |
104 goto err; | |
105 | |
106 FILETIME creationTime; | |
107 FILETIME accessTime; | |
108 FILETIME writeTime; | |
109 | |
110 BOOL b = GetFileTime(h, &creationTime, &accessTime, &writeTime); | |
111 if(b==1){ | |
112 long modA = writeTime.dwLowDateTime; | |
113 long modB = writeTime.dwHighDateTime; | |
114 return modA | (modB << (writeTime.dwHighDateTime.sizeof*8)); | |
115 } | |
116 | |
117 err: | |
118 CloseHandle(h); | |
119 throw new Exception("failed to query file modification : "~fileName); | |
120 }else version(Unix){ | |
121 char* namez = toStringz(fileName); | |
122 struct_stat statbuf; | |
123 | |
124 if(stat(namez, &statbuf)){ | |
125 throw new FileException(fileName, getErrno()); | |
126 } | |
127 | |
128 return statbuf.st_mtime; | |
129 }else{ | |
130 static assert(0); | |
131 } | |
132 } | |
133 } | |
134 | |
135 char[] cleanFileName(char[] file){ | |
136 char[] back; | |
137 bool hadSep; | |
138 | |
139 foreach(char c; file){ | |
140 if(c == '/' || c == '\\'){ | |
141 if(!hadSep){ | |
142 back ~= '/'; | |
143 hadSep = true; | |
144 } | |
145 }else{ | |
146 back ~= c; | |
147 hadSep = false; | |
148 } | |
149 } | |
150 | |
151 size_t start = 0; | |
152 while(back[start] <= ' ' && start < back.length){ | |
153 start++; | |
154 } | |
155 | |
156 size_t end = back.length-1; | |
157 while(back[end] <= ' ' && end >= start){ | |
158 end--; | |
159 } | |
160 | |
161 back = back[start .. end+1]; | |
162 | |
163 return back; | |
164 } | |
165 | |
166 class Test{ | |
167 char[] name; | |
168 char[] file; | |
169 Result r; | |
170 | |
171 this(char[] file){ | |
172 this.file = file; | |
173 | |
174 int start = rfind(file, "/"); | |
175 if(start<0){ | |
176 start = 0; | |
177 }else{ | |
178 start += 1; | |
179 } | |
180 | |
181 int end = rfind(file, "."); | |
182 if(end < start){ | |
183 end = file.length; | |
184 } | |
185 | |
186 name = file[start .. end]; | |
187 } | |
188 } | |
189 | |
190 | |
191 class Log{ | |
192 Test[char[]] tests; | |
193 | |
194 char[] id; | |
195 | |
196 int[Result] counts; | |
197 | |
198 this(char[] id, char[] file){ | |
199 this.id = id; | |
200 counts = [ | |
201 Result.PASS: 0, | |
202 Result.FAIL: 0, | |
203 Result.XPASS: 0, | |
204 Result.XFAIL: 0, | |
205 Result.ERROR: 0 ]; | |
206 | |
207 writefln("parsing: %s", file); | |
208 FStime logTime = getFStime(file); | |
209 Stream source = new BufferedFile(file, FileMode.In); | |
210 while(!source.eof()){ | |
211 add(source.readLine()); | |
212 } | |
213 dropBogusResults(logTime, "dstress"); | |
214 } | |
215 | |
216 | |
217 void dropBogusResults(FStime recordTime, char[] testRoot){ | |
218 uint totalCount = tests.length; | |
219 | |
220 char[][] sourcesTests = tests.keys; | |
221 foreach(char[] source; sourcesTests){ | |
222 if(find(source, "complex/") < 0){ | |
223 try{ | |
224 FStime caseTime = getFStime(testRoot~std.path.sep~source); | |
225 if(caseTime > recordTime){ | |
226 debug(drop) fwritefln(stderr, "dropped: %s", source); | |
227 counts[tests[source].r & Result.BASE_MASK]--; | |
228 tests.remove(source); | |
1127
3e98925bcc39
Fix makewebstatistics: don't try to drop a test multiple times.
Frits van Bommel <fvbommel wxs.nl>
parents:
663
diff
changeset
|
229 continue; |
255 | 230 } |
231 }catch(Exception e){ | |
232 debug(drop) fwritefln(stderr, "dropped: %s", source); | |
233 counts[tests[source].r & Result.BASE_MASK]--; | |
234 tests.remove(source); | |
1127
3e98925bcc39
Fix makewebstatistics: don't try to drop a test multiple times.
Frits van Bommel <fvbommel wxs.nl>
parents:
663
diff
changeset
|
235 continue; |
255 | 236 } |
237 } | |
238 // asm-filter | |
239 int i = find(source, "asm_p"); | |
240 if(i >= 0){ | |
241 counts[tests[source].r & Result.BASE_MASK]--; | |
242 tests.remove(source); | |
1127
3e98925bcc39
Fix makewebstatistics: don't try to drop a test multiple times.
Frits van Bommel <fvbommel wxs.nl>
parents:
663
diff
changeset
|
243 continue; |
255 | 244 } |
245 } | |
246 tests.rehash; | |
247 | |
248 writefln("dropped %s outdated tests (%s remaining)", totalCount - tests.length, tests.length); | |
249 } | |
250 | |
251 | |
252 bool add(char[] line){ | |
253 const char[] SUB = "Torture-Sub-"; | |
254 const char[] TORTURE = "Torture:"; | |
255 | |
256 line = strip(line); | |
257 int id = -1; | |
258 Result r = Result.UNTESTED; | |
259 | |
260 if(line.length > SUB.length && line[0 .. SUB.length] == SUB){ | |
261 line = line[SUB.length .. $]; | |
262 id = 0; | |
263 while(line[id] >= '0' && line[id] <= '9'){ | |
264 id++; | |
265 } | |
266 int start = id; | |
267 id = std.conv.toUint(line[0 .. id]); | |
268 | |
269 while(line[start] != '-'){ | |
270 start++; | |
271 } | |
272 line = line[start+1 .. $]; | |
273 } | |
274 | |
275 char[][] token = split(line); | |
276 if(token.length < 2){ | |
277 return false; | |
278 } | |
279 char[] file = strip(token[1]); | |
280 | |
281 switch(token[0]){ | |
282 case "PASS:": | |
283 r = Result.PASS; break; | |
284 case "FAIL:": | |
285 r = Result.FAIL; break; | |
286 case "XPASS:": | |
287 r = Result.XPASS; break; | |
288 case "XFAIL:": | |
289 r = Result.XFAIL; break; | |
290 case "ERROR:": | |
291 r = Result.ERROR; break; | |
292 default:{ | |
293 if(token[0] == TORTURE){ | |
294 throw new Exception("not yet handled: "~line); | |
295 }else if(id > -1){ | |
296 throw new Exception(format("bug in SUB line: (%s) %s", id, line)); | |
297 } | |
298 } | |
299 } | |
300 | |
301 if(r != Result.UNTESTED){ | |
302 if(std.string.find(line, "bad error message") > -1){ | |
303 r |= Result.BAD_MSG; | |
304 } | |
305 if(std.string.find(line, "bad debugger message") > -1){ | |
306 r |= Result.BAD_MSG; | |
307 } | |
308 | |
309 file = cleanFileName(file); | |
310 | |
311 if(id >= 0){ | |
312 // update sub | |
313 id--; | |
314 | |
315 Test* test = file in tests; | |
316 | |
317 if(test is null){ | |
318 Test t = new Test(file); | |
319 tests[file] = t; | |
320 t.r = r; | |
321 counts[r & Result.BASE_MASK]++; | |
322 }else{ | |
323 if(test.r != Result.UNTESTED){ | |
324 test.r = Result.UNTESTED; | |
325 } | |
326 test.r = r; | |
327 } | |
328 } | |
329 return true; | |
330 } | |
331 return false; | |
332 } | |
333 } | |
334 | |
335 | |
266 | 336 char[] basedir = "web"; |
337 bool regenerate = false; | |
338 | |
255 | 339 int main(char[][] args){ |
340 | |
266 | 341 if(args.length < 3 || (args[1] == "--regenerate" && args.length < 4)){ |
342 fwritefln(stderr, "%s [--regenerate] <reference-log> <log> <log> ...", args[0]); | |
663
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
343 fwritefln(stderr, "bash example: %s reference/dmd-something $(ls reference/ldc*)", args[0]); |
255 | 344 return 1; |
345 } | |
346 | |
266 | 347 char[] reference; |
257 | 348 char[][] files; |
349 if(args[1] == "--regenerate") { | |
350 regenerate = true; | |
266 | 351 reference = args[2]; |
352 files = args[3..$] ~ reference; | |
257 | 353 } else { |
266 | 354 reference = args[1]; |
355 files = args[2..$] ~ reference; | |
257 | 356 } |
357 | |
255 | 358 // make sure base path exists |
359 if(std.file.exists(basedir) && !std.file.isdir(basedir)) | |
360 throw new Exception(basedir ~ " is not a directory!"); | |
361 else if(!std.file.exists(basedir)) | |
362 std.file.mkdir(basedir); | |
363 | |
364 | |
365 Log[char[]] logs; | |
366 | |
266 | 367 // emit per-log data |
368 foreach(char[] file; files) | |
369 generateLogStatistics(file, logs); | |
370 | |
371 // differences between logs | |
372 foreach(int i, char[] file; files[1 .. $]) | |
373 generateChangeStatistics(files[1+i], files[1+i-1], logs); | |
374 | |
375 // differences between reference and logs | |
376 foreach(char[] file; files[0..$-1]) | |
377 generateChangeStatistics(file, reference, logs); | |
378 | |
379 // collect all the stats.base files into a large table | |
380 BufferedFile index = new BufferedFile(std.path.join(basedir, "index.html"), FileMode.OutNew); | |
381 scope(exit) index.close(); | |
382 index.writefln(` | |
663
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
383 <!DOCTYPE html> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
384 <html> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
385 <head> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
386 <title>DStress results for x86-32 Linux</title> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
387 <style type="text/css"> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
388 body { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
389 font-family: Arial, Helvetica, sans-serif; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
390 font-size: 0.8em; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
391 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
392 a { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
393 text-decoration: none; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
394 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
395 a:hover { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
396 border-bottom: 1px dotted blue; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
397 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
398 table { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
399 border-collapse: collapse; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
400 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
401 tr { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
402 border-bottom: 1px solid #CCC; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
403 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
404 tr.odd { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
405 background: #e0e0e0; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
406 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
407 tr.head { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
408 border-bottom: none; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
409 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
410 td,th { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
411 padding: 2px 10px 2px 10px; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
412 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
413 .result:hover { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
414 background: #C3DFFF; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
415 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
416 .pass,.xfail,.xpass,.fail,.xpass,.error,.generic { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
417 text-align: center; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
418 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
419 .generic { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
420 background: #EEE; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
421 color: gray; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
422 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
423 .pass { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
424 background: #98FF90; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
425 color: green; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
426 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
427 tr:hover .pass { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
428 background: #83E67B; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
429 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
430 .xfail { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
431 background: #BDFFB8; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
432 color: #0CAE00; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
433 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
434 tr:hover .xfail { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
435 background: #98FF90; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
436 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
437 .fail { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
438 background: #FF6E7A; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
439 color: maroon; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
440 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
441 .xpass { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
442 background: #FF949D; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
443 color: maroon; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
444 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
445 .error { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
446 background: #FFB3B9; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
447 color: maroon; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
448 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
449 .borderleft { |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
450 border-left: 1px solid #CCC; |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
451 } |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
452 </style> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
453 </head> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
454 |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
455 <body> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
456 <h1>DStress results for x86-32 Linux</h1> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
457 |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
458 <h2>Legend</h2> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
459 <table id="legend"> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
460 <tr> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
461 <th>Color</th> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
462 <th>Description</th> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
463 </tr> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
464 <tr class="result"> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
465 <td class="pass">PASS</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
466 <td>Test passed and was expected to pass</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
467 </tr> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
468 <tr class="result"> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
469 <td class="xfail">XFAIL</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
470 <td>Test failed and expected to fail</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
471 </tr> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
472 <tr class="result"> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
473 <td class="fail">FAIL</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
474 <td>Test failed but was expected to pass</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
475 </tr> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
476 <tr class="result"> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
477 <td class="xpass">XPASS</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
478 <td>Test passed but was expected to fail</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
479 </tr> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
480 <tr class="result"> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
481 <td class="error">ERROR</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
482 <td>The compiler, linker or the test segfaulted</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
483 </tr> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
484 <tr class="result"> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
485 <td class="generic">+</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
486 <td>Changes from FAIL, XPASS or ERROR to PASS or XFAIL</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
487 </tr> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
488 <tr class="result"> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
489 <td class="generic">-</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
490 <td>Changes from PASS or XFAIL to FAIL, XPASS or ERROR</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
491 </tr> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
492 <tr class="result"> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
493 <td class="generic">chg</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
494 <td>Changed within the good or bad group without crossing over</td> |
6aaa3d3c1183
First part of rename to LDC.
Christian Kamm <kamm incasoftware de>
parents:
552
diff
changeset
|
495 </tr> |
497
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
496 </table> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
497 |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
498 <h2>Results</h2> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
499 <table> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
500 <tr class="head"> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
501 <th></th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
502 <th colspan="5" class="borderleft">Test results</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
503 <th colspan="3" class="borderleft">Diff to previous</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
504 <th colspan="3" class="borderleft">Diff to ` ~ std.path.getBaseName(reference) ~ `</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
505 </tr> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
506 <tr> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
507 <th>Name</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
508 <th class="borderleft">PASS</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
509 <th>XFAIL</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
510 <th>FAIL</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
511 <th>XPASS</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
512 <th>ERROR</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
513 <th class="borderleft">+</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
514 <th>-</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
515 <th>chg</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
516 <th class="borderleft">+</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
517 <th>-</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
518 <th>chg</th> |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
519 </tr> |
266 | 520 `); |
255 | 521 |
266 | 522 for(int i = files.length - 1; i >= 0; --i) { |
523 auto file = files[i]; | |
497
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
524 index.writefln(`<tr class="` ~ (i%2 ? `result` : `odd result`) ~ `">`); |
266 | 525 char[] id = std.path.getBaseName(file); |
526 char[] statsname = std.path.join(std.path.join(basedir, id), "stats.base"); | |
527 index.writef(cast(char[])std.file.read(statsname)); | |
528 | |
497
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
529 if(i != 0) { |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
530 char[] newid = std.path.getBaseName(files[i-1]); |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
531 statsname = std.path.join(std.path.join(basedir, newid ~ "-to-" ~ id), "stats.base"); |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
532 index.writef(cast(char[])std.file.read(statsname)); |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
533 } else { |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
534 index.writefln(`<td class="borderleft"></td><td></td><td></td>`); |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
535 } |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
536 |
266 | 537 if(i != files.length - 1) { |
538 char[] refid = std.path.getBaseName(reference); | |
539 statsname = std.path.join(std.path.join(basedir, refid ~ "-to-" ~ id), "stats.base"); | |
540 index.writef(cast(char[])std.file.read(statsname)); | |
541 } else { | |
497
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
542 index.writefln(`<td class="borderleft"></td><td></td><td></td>`); |
266 | 543 } |
544 | |
497
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
545 index.writefln(`</tr>`); |
266 | 546 } |
547 | |
548 index.writefln(`</table></body></html>`); | |
549 | |
550 return 0; | |
551 } | |
552 | |
553 void generateLogStatistics(char[] file, ref Log[char[]] logs) | |
554 { | |
555 char[] id = std.path.getBaseName(file); | |
556 char[] dirname = std.path.join(basedir, id); | |
557 | |
558 if(std.file.exists(dirname)) { | |
559 if(std.file.isdir(dirname)) { | |
560 if(!regenerate) { | |
561 writefln("Directory ", dirname, " already exists, skipping..."); | |
562 return; | |
255 | 563 } |
564 } | |
565 else | |
266 | 566 throw new Exception(dirname ~ " is not a directory!"); |
567 } | |
568 else | |
569 std.file.mkdir(dirname); | |
255 | 570 |
266 | 571 // parse etc. |
572 Log log = new Log(id, file); | |
573 logs[id] = log; | |
255 | 574 |
266 | 575 // write status |
576 { | |
255 | 577 BufferedFile makeFile(char[] name) { |
261
5723b7385c25
[svn r279] fixed bug in makewebstatistics, regenerated output
ChristianK
parents:
257
diff
changeset
|
578 return new BufferedFile(std.path.join(dirname, name), FileMode.OutNew); |
255 | 579 } |
580 BufferedFile[Result] resultsfile = [ | |
581 Result.PASS: makeFile("pass.html"), | |
582 Result.FAIL: makeFile("fail.html"), | |
583 Result.XPASS: makeFile("xpass.html"), | |
584 Result.XFAIL: makeFile("xfail.html"), | |
585 Result.ERROR: makeFile("error.html") ]; | |
266 | 586 |
255 | 587 scope(exit) { |
588 foreach(file; resultsfile) | |
589 file.close(); | |
590 } | |
266 | 591 |
592 foreach(file; resultsfile) | |
593 file.writefln(`<html><body>`); | |
594 | |
255 | 595 foreach(tkey; log.tests.keys.sort) { |
596 auto test = log.tests[tkey]; | |
597 auto result = test.r & Result.BASE_MASK; | |
266 | 598 resultsfile[result].writefln(test.name, " in ", test.file, "<br>"); |
255 | 599 } |
600 | |
266 | 601 foreach(file; resultsfile) |
602 file.writefln(`</body></html>`); | |
603 } | |
255 | 604 |
266 | 605 BufferedFile stats = new BufferedFile(std.path.join(dirname, "stats.base"), FileMode.OutNew); |
606 scope(exit) stats.close(); | |
497
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
607 stats.writefln(`<td>`, id, `</td>`); |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
608 stats.writefln(`<td class="pass borderleft"><a href="`, std.path.join(log.id, "pass.html"), `">`, log.counts[Result.PASS], `</a></td>`); |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
609 stats.writefln(`<td class="xfail"><a href="`, std.path.join(log.id, "xfail.html"), `">`, log.counts[Result.XFAIL], `</a></td>`); |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
610 stats.writefln(`<td class="fail"><a href="`, std.path.join(log.id, "fail.html"), `">`, log.counts[Result.FAIL], `</a></td>`); |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
611 stats.writefln(`<td class="xpass"><a href="`, std.path.join(log.id, "xpass.html"), `">`, log.counts[Result.XPASS], `</a></td>`); |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
612 stats.writefln(`<td class="error"><a href="`, std.path.join(log.id, "error.html"), `">`, log.counts[Result.ERROR], `</a></td>`); |
266 | 613 } |
255 | 614 |
266 | 615 void generateChangeStatistics(char[] file1, char[] file2, ref Log[char[]] logs) |
616 { | |
617 char[] newid = std.path.getBaseName(file1); | |
618 char[] oldid = std.path.getBaseName(file2); | |
619 | |
620 char[] dirname = std.path.join(basedir, oldid ~ "-to-" ~ newid); | |
621 | |
622 if(std.file.exists(dirname)) { | |
623 if(std.file.isdir(dirname)) { | |
624 if(!regenerate) { | |
625 writefln("Directory ", dirname, " already exists, skipping..."); | |
626 return; | |
255 | 627 } |
628 } | |
629 else | |
266 | 630 throw new Exception(dirname ~ " is not a directory!"); |
631 } | |
632 else | |
633 std.file.mkdir(dirname); | |
255 | 634 |
266 | 635 // parse etc. |
636 Log newLog, oldLog; | |
637 Log getOrParse(char[] id, char[] file) { | |
638 if(id in logs) | |
639 return logs[id]; | |
640 else { | |
641 Log tmp = new Log(id, file); | |
642 logs[id] = tmp; | |
643 return tmp; | |
255 | 644 } |
266 | 645 } |
646 newLog = getOrParse(newid, file1); | |
647 oldLog = getOrParse(oldid, file2); | |
255 | 648 |
266 | 649 int nRegressions, nImprovements, nChanges; |
650 | |
651 { | |
261
5723b7385c25
[svn r279] fixed bug in makewebstatistics, regenerated output
ChristianK
parents:
257
diff
changeset
|
652 auto regressionsFile = new BufferedFile(std.path.join(dirname, "regressions.html"), FileMode.OutNew); |
255 | 653 scope(exit) regressionsFile.close(); |
266 | 654 regressionsFile.writefln(`<html><body>`); |
655 | |
261
5723b7385c25
[svn r279] fixed bug in makewebstatistics, regenerated output
ChristianK
parents:
257
diff
changeset
|
656 auto improvementsFile = new BufferedFile(std.path.join(dirname, "improvements.html"), FileMode.OutNew); |
255 | 657 scope(exit) improvementsFile.close(); |
266 | 658 improvementsFile.writefln(`<html><body>`); |
659 | |
261
5723b7385c25
[svn r279] fixed bug in makewebstatistics, regenerated output
ChristianK
parents:
257
diff
changeset
|
660 auto changesFile = new BufferedFile(std.path.join(dirname, "changes.html"), FileMode.OutNew); |
255 | 661 scope(exit) changesFile.close(); |
266 | 662 changesFile.writefln(`<html><body>`); |
663 | |
255 | 664 BufferedFile targetFile; |
266 | 665 |
346
c9d5c711d65a
[svn r367] In web dstress statistics, write change lists sorted by filename.
ChristianK
parents:
266
diff
changeset
|
666 foreach(file; newLog.tests.keys.sort){ |
c9d5c711d65a
[svn r367] In web dstress statistics, write change lists sorted by filename.
ChristianK
parents:
266
diff
changeset
|
667 Test* t = file in newLog.tests; |
c9d5c711d65a
[svn r367] In web dstress statistics, write change lists sorted by filename.
ChristianK
parents:
266
diff
changeset
|
668 Test* oldT = file in oldLog.tests; |
255 | 669 |
670 if(oldT !is null){ | |
671 if(oldT.r == t.r) | |
672 continue; | |
552
648409a7fb0c
Fix findregressions and makewebstatistics to adhere to our definition of
Christian Kamm <kamm incasoftware de>
parents:
497
diff
changeset
|
673 else if(t.r >= Result.XPASS && oldT.r && oldT.r <= Result.XFAIL){ |
255 | 674 targetFile = regressionsFile; |
675 nRegressions++; | |
676 } | |
552
648409a7fb0c
Fix findregressions and makewebstatistics to adhere to our definition of
Christian Kamm <kamm incasoftware de>
parents:
497
diff
changeset
|
677 else if(t.r && t.r <= Result.XFAIL && oldT.r >= Result.XPASS){ |
255 | 678 targetFile = improvementsFile; |
679 nImprovements++; | |
680 } | |
681 else { | |
682 targetFile = changesFile; | |
683 nChanges++; | |
684 } | |
266 | 685 targetFile.writefln(toString(oldT.r), " -> ", toString(t.r), " : ", t.name, " in ", t.file, "<br>"); |
255 | 686 } |
266 | 687 } |
255 | 688 |
266 | 689 regressionsFile.writefln(`</body></html>`); |
690 improvementsFile.writefln(`</body></html>`); | |
691 changesFile.writefln(`</body></html>`); | |
255 | 692 } |
693 | |
266 | 694 BufferedFile stats = new BufferedFile(std.path.join(dirname, "stats.base"), FileMode.OutNew); |
695 scope(exit) stats.close(); | |
696 auto dir = oldid ~ "-to-" ~ newid; | |
497
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
697 stats.writefln(`<td class="borderleft"><a href="`, std.path.join(dir, "improvements.html"), `">`, nImprovements, `</a></td>`); |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
698 stats.writefln(`<td><a href="`, std.path.join(dir, "regressions.html"), `">`, nRegressions, `</a></td>`); |
70faa6af1357
Change web statistics layout, thanks anders!
Christian Kamm <kamm incasoftware de>
parents:
362
diff
changeset
|
699 stats.writefln(`<td><a href="`, std.path.join(dir, "changes.html"), `">`, nChanges, `</a></td>`); |
255 | 700 } |