6c25938c8d23ee9ce586458440127d2f25ea3a78
[framework/graphics/cairo.git] / test / testtable.js
1 /* configuration */
2 /* TODO: UNTESTED count can't be shown because it's not tracked explicitly */
3 headerResults = [ "PASS", "NEW", "FAIL", "XFAIL", "CRASHED" ];
4 logResults    = [ "PASS", "NEW", "FAIL", "XFAIL", "CRASH!" ];
5 resultToImgs = {
6     "PASS"     : [],
7     "NEW"      : [ "output" ],
8     "FAIL"     : [ "output", "difference", "reference" ],
9     "XFAIL"    : [],
10     "UNTESTED" : [],
11     "CRASHED"  : []
12 };
13
14 resultToString = {
15     "PASS"     : "",
16     "NEW"      : "",
17     "FAIL"     : "",
18     "XFAIL"    : "",
19     "UNTESTED" : "",
20     "CRASHED"  : "CRASHED!"
21 };
22
23 resultField = "result";
24 rowFields = [ "test", "offset", "similar" ];
25 colFields = [ "target", "format" ];
26 allFields = [ resultField ].concat (rowFields, colFields);
27
28
29 /* globals: */
30 function resetGlobals () {
31     dragElement = undefined;
32     table = document.getElementById ("testTable");
33     while (table.rows.length)
34         table.deleteRow (0);
35     colsArray = [ "HrowHeader" ];
36     colsMap = undefined;
37     headerId = "HcolHeader";
38
39     fTrue = function (x) { return true; };
40
41     empty = new Row ();
42     header = new Row ();
43     header[colsArray[0]].toString = function () { return ""; };
44
45     untested = new Test ();
46     untested[resultField] = "UNTESTED";
47 }
48
49
50 /* utility functions */
51 function isKey (key) { return key[key.length-1] == ':'; }
52 function normalizeKey (key) { return key.toLowerCase ().replace (/[^a-z0-9]/, ""); }
53 function isVisible (x) { return x.style.display != "none"; }
54
55 function link (html, url) { return "<a href='" + url + "'>" + html + "</a>"; }
56 function image (url) { return "<img src='" + url + "'>"; }
57 function span (html, id, cls) { return "<span id='" + id + "' class='" + cls + "' onmousedown='startDrag (event)' onmouseup='mouseUp (event)'>" + html + "</span>"; }
58
59 function fieldsToHTML (bColumns, values) {
60     var fields = bColumns ? colFields : rowFields;
61     var prefix = bColumns ? "c" : "r";
62     var tmpRE = arrayApply (function (x) { return "[^/]*"; }, fields);
63     var r = Array ();
64     for (var i = 0; i < fields.length; i++)
65         if (fields[i] == "test") {
66             r.push (link (values[fields[i]], "output/" + values[fields[i]] + ".log"));
67         } else {
68             tmpRE[i] = values[fields[i]];
69             r.push (span (values[fields[i]], prefix + "/" + tmpRE.join ("/") + "/", fields[i]));
70             tmpRE[i] = "[^/]*";
71         }
72     return r.join ("/");
73 }
74
75 function inArray (value, array) {
76     for (var i = 0; i < array.length; i++)
77         if (value == array[i])
78             return true;
79     return false;
80 }
81
82 function arrayApply (fun, array) {
83     var r = new Array ();
84     for (var i = 0; i < array.length; i++)
85         r.push (fun(array[i]));
86     return r;
87 }
88
89 function arrayPred (pred, array) {
90     var r = new Array ();
91     for (var i = 0; i < array.length; i++)
92         if (pred (array[i]))
93             r.push (array[i]);
94     return r;
95 }
96
97 function arrayMap (map, array) { return arrayApply (function (x) { return map[x]; }, array); }
98
99 function binSearch (rows, newId){
100     var min = 0;
101     var max = rows.length;
102
103     while (max - min > 1) {
104         var mid = (max + min) >> 1;
105         if (rows[mid].id > newId)
106             max = mid;
107         else
108             min = mid;
109     }
110
111     if (max == min)
112         return max;
113     else
114         return rows[min].id > newId ? min : max;
115 }
116
117 /* dynamic table utils */
118 function updateCurrent () {
119     for (var i = 0; i < table.rows.length; i++) {
120         var row = table.rows[i];
121         if (isVisible (row)) {
122             /* j starts from 1 because we want to ignore _rowHeader */
123             for (var j = 1; j < row.cells.length; j++)
124                 if (row.id[0] == "H")
125                     for (var k = 0; k < headerResults.length; k++)
126                         header[row.cells[j].id].current[headerResults[k]] = 0;
127                 else if (isVisible (row.cells[j]))
128                     header[row.cells[j].id].current[row.cells[j].className]++;
129         }
130     }
131
132     updateHeader ();
133 }
134
135 function setVisible (array, subsetPred, visibilityPred, visibleFlag) {
136     var modified = false, somethingVisible = false;
137     for (var i = 0; i < array.length; i++)
138         if (array[i].id[0] != "H") {
139             if (subsetPred (array[i])) {
140                 var wanted = visibilityPred (array[i]);
141                 if (isVisible (array[i]) != wanted) {
142                     modified = true;
143                     array[i].style.display = wanted ? visibleFlag : "none";
144                 }
145             }
146             somethingVisible = somethingVisible || isVisible (array[i]);
147         }
148     return modified && somethingVisible;
149 }
150
151 function setVisibleOnly (array, pred, visibleFlag) {
152     return setVisible (array, fTrue, pred, visibleFlag);
153 }
154
155 function flipVisible (array, subsetPred, visibleFlag) {
156     return setVisible (array, subsetPred, function (x) { return !isVisible (x); }, visibleFlag);
157 }
158
159
160 /* event handling */
161 function ignoreEvent (event) {
162     if (event.preventDefault)
163         event.preventDefault();
164     else
165         event.returnValue= false;
166     return false;
167 }
168
169 function mouseUp (event) {
170     var visFun;
171     if (event.button == 0)
172         visFun = setVisibleOnly;
173     else if (event.button == 2)
174         visFun = flipVisible;
175     else
176         return false;
177
178     var structureFun;
179     if (event.target.id[0] == "r") /* rows */
180         structureFun = function (f, p) { return f (table.rows, p, "table-row"); };
181     else if (event.target.id[0] == "c") /* cols */
182         structureFun = function (f, p) { return inArray (true, arrayApply (function (row) { return f (row.cells, p, "table-cell") }, table.rows)) };
183     else
184         return false;
185
186     var pred;
187     if (event.target.id[1] == "/") { /* regexp */
188         var re = new RegExp (event.target.id);
189         pred = function (x) { return re.test (x.id); };
190     } else if (event.target.id[1] == "#") { /* counters */
191         var s = event.target.id.substr (2).split ("/");
192         pred = function (row) { return row.cells[s[0]].className == s[1]; }
193     } else
194         return false;
195
196     if (!structureFun (visFun, pred))
197         if (!structureFun (flipVisible, fTrue))
198             structureFun (flipVisible, fTrue);
199
200     updateCurrent ();
201
202     return false;
203 }
204
205 function noDrag (event) {
206     dragElement = undefined;
207     return false;
208 }
209
210 function startDrag (event) {
211     if (event.button == 0)
212         dragElement = event.target;
213     else
214         dragElement = undefined;
215     return false;
216 }
217
218 function endDrag (event) {
219     if (!dragElement)
220         return false;
221
222     if (event.currentTarget.id == colsArray[0] &&
223         inArray (dragElement.className, colFields)) {
224         rowFields.push (dragElement.className);
225         colFields = arrayPred (function (x) { return x != dragElement.className; }, colFields);
226     } else if (event.currentTarget.id == headerId &&
227                inArray (dragElement.className, rowFields)) {
228         colFields.push (dragElement.className);
229         rowFields = arrayPred (function (x) { return x != dragElement.className; }, rowFields);
230     } else
231         return true;
232
233     reloadAll ();
234     return false;
235 }
236
237
238 /* table content */
239 function Row (id, t) {
240     this[colsArray[0]] = new RowHeader (id, t);
241
242     this.get = function (c) { return this[c] != undefined ? this[c] : untested; }
243     this.getHTML = function (c) { return this.get(c).toString (); };
244     this.setStyle = function (c, element) { return this.get(c).setStyle (element); };
245 }
246
247 function ColumnHeader (id, values) {
248     this.id = id;
249     this.values = values;
250     this.total = new Object ();
251     this.current = new Object ();
252
253     for (var i = 0; i < headerResults.length; i++) {
254         this.total[headerResults[i]] = 0;
255         this.current[headerResults[i]] = 0;
256     }
257
258     this.toString = function () {
259         var counts = new Array ();
260         for (var i = 0; i < headerResults.length; i++) {
261             var hr = headerResults[i];
262             var s = span (this.current[hr], "r#" + colsMap[this.id] + "/" + hr, hr);
263             if (this.current[hr] != this.total[hr])
264                 s += span ("[" + this.total[hr] + "]", "r#" + colsMap[this.id] + "/" + hr, hr);
265             counts.push (s);
266         }
267
268         return fieldsToHTML (true, this.values) + "<br>" + counts.join ("/");
269     }
270
271     this.setStyle = function (element) { };
272 }
273
274 function RowHeader (id, values) {
275     this.id = id;
276     this.values = values;
277     this.toString = function () { return fieldsToHTML (false, this.values); }
278     this.setStyle = function (element) { element.onmouseup = endDrag; };
279 }
280
281 function Test () {
282     this.rowId = function () { return "r/" + arrayMap (this, rowFields).join("/") + "/"; };
283     this.colId = function () { return "c/" + arrayMap (this, colFields).join("/") + "/"; };
284     this.isComplete = function () { return !inArray (undefined, arrayMap (this, allFields)); }
285     this.toString = function () {
286         var images = arrayMap (this, resultToImgs[this[resultField]]);
287         images = arrayPred (function (x) { return x != undefined; }, images);
288         images = arrayApply (function (x) { return link (image (x), x); }, images);
289         images.push (resultToString[this[resultField]]);
290         return images.join (" ");
291     };
292
293     this.setStyle = function (element) { element.className = this[resultField]; };
294
295     this.addData = function (array) {
296         for (var i = 0; i < array.length - 1; i += 2)
297             if (isKey (array[i]))
298                 this[normalizeKey (array[i])] = array[i+1];
299     };
300 }
301
302
303 /* table creation */
304 function insertCell (domRow, nid, tests) {
305     var domCell = domRow.insertCell (nid);
306     domCell.id = colsArray[nid];
307     domCell.innerHTML = tests.getHTML (colsArray[nid]);
308     tests.setStyle (colsArray[nid], domCell);
309 }
310
311 function updateRow (row, tests) {
312     var domRow = document.getElementById (row);
313     if (!domRow) {
314         domRow = table.insertRow (binSearch (table.rows, row));
315         domRow.id = row;
316     }
317
318     for (var i = 0; i < colsArray.length; i++)
319         if (i >= domRow.cells.length || domRow.cells[i].id != colsArray[i])
320             insertCell (domRow, i, tests);
321 }
322
323 function updateHeader () {
324     var visibility;
325     var domRow = document.getElementById (headerId);
326     if (domRow) {
327         visibility = new Object ();
328         for (var i = 0; i < domRow.cells.length; i++)
329             visibility[domRow.cells[i].id] = domRow.cells[i].style.display;
330         table.deleteRow (domRow.rowIndex);
331     }
332
333     updateRow (headerId, header);
334     table.rows[0].onmouseup = endDrag;
335
336     if (visibility)
337         for (var i = 0; i < colsArray.length; i++)
338             if (visibility[colsArray[i]])
339                 table.rows[0].cells[colsMap[colsArray[i]]].style.display = visibility[colsArray[i]];
340 }
341
342 function updateTable () {
343     colsArray.sort ();
344
345     colsMap = new Object ();
346     for (var i = 0; i < colsArray.length; i++)
347         colsMap[colsArray[i]] = i;
348
349     updateHeader ();
350     for (var i = 0; i < table.rows.length; i++)
351         updateRow (table.rows[i].id, empty);
352 }
353
354
355 /* log file parsing */
356 function parseTest (testData) {
357     var colsChanged = false;
358     var rows = new Array ();
359     var data = new Object ();
360     var t = new Test ();
361     var lines = testData.replace (/\r/g, "").split ("\n");
362     for (var i = 0; i < lines.length; i++) {
363         t.addData (lines[i].split (" "));
364         if (t.isComplete ()) {
365             var c = t.colId ();
366             if (header[c] == undefined) {
367                 colsArray.push (c);
368                 header[c] = new ColumnHeader (c, t);
369                 colsChanged = true;
370             }
371
372             var r = t.rowId ();
373             if (!data[r]) {
374                 rows.push (r);
375                 data[r] = new Row (r, t);
376             }
377
378             data[r][c] = t;
379             header[c].total[t[resultField]]++;
380             header[c].current[t[resultField]]++;
381             t = new Test ();
382         }
383     }
384
385     if (colsChanged)
386         updateTable ();
387     else
388         updateHeader ();
389
390     for (var i = 0; i < rows.length; i++)
391         updateRow (rows[i], data[rows[i]]);
392 }
393
394 function parseFile (fileName, parser) {
395     var req = new XMLHttpRequest ();
396     req.onreadystatechange = function () {
397         if (req.readyState == 4)
398             parser (req.responseText);
399     }
400
401     try {
402         req.open ("GET", fileName);
403         req.send (null);
404     } catch (e) {}
405
406
407 function parseTestList (listData) {
408     var summaryRE = /\d+ Passed, \d+ Failed \x5b\d+ crashed, \d+ expected\x5d, \d+ Skipped/;
409     var lines = listData.replace (/\r/g, "").split ("\n");
410     for (var i = 0; i < lines.length; i++) {
411         if (summaryRE.test (lines[i]))
412             return;
413         
414         var words = lines[i].split (" ");
415         if (words.length >= 2 &&
416             words[0][words[0].length-1] == ":" &&
417             inArray (words[1], logResults))
418             parseFile ("output/" + words[0].substr (0, words[0].length-1) + ".log", parseTest);
419     }
420 }
421
422 function reloadAll() {
423     resetGlobals ();
424
425     parseFile ("cairo-test-suite.log", parseTestList);
426
427
428 window.onload = reloadAll;