summaryrefslogtreecommitdiff
path: root/test/testtable.js
diff options
context:
space:
mode:
authorAndrea Canciani <ranma42@gmail.com>2010-08-06 18:23:07 +0200
committerAndrea Canciani <ranma42@gmail.com>2010-10-07 23:09:13 +0200
commitf29e7d155f3a2ee75daf63719460348ea2ca0f76 (patch)
treee055301dafe8afe191a97cb5f936fc2835f3aaa0 /test/testtable.js
parent4e064b3a32e4d699a6494bf9d8dbcd7b8d9cbc64 (diff)
downloadcairo-f29e7d155f3a2ee75daf63719460348ea2ca0f76.tar.gz
test: Add a new test result html page
This page uses JavaScript to parse test log files and create the test table according to the results. It also allows dynamic selection and hiding of rows/columns based on a chosen parameter and table structure change, by dragging a field from rows to columns and vice versa. Left click selects the cells with the chosen parameter-value association. If these cells are exactly the only show cells, it hides them and shows all the other ones, instead. Right click inverts the visibility of the cells with the chosen parameter-value association. When some rows are hidden, the PASS/NEW/FAIL/XFAIL/CRASH counters show both the currently shown test case count and the total count, if they are different: "23[62]" means that there are 62 test case in that category, but only 23 are currently visible. Dragging a field from the row (or column) header to the column (or row) header rebuilds the table to have that field along the columns (or rows), updating PASS/NEW/FAIL/... counters and showing the whole table again. Test names are hyperlinks to the test log. Images are hyperlinks to themselves.
Diffstat (limited to 'test/testtable.js')
-rw-r--r--test/testtable.js426
1 files changed, 426 insertions, 0 deletions
diff --git a/test/testtable.js b/test/testtable.js
new file mode 100644
index 000000000..0a6a6dd1a
--- /dev/null
+++ b/test/testtable.js
@@ -0,0 +1,426 @@
+/* configuration */
+/* TODO: UNTESTED count can't be shown because it's not tracked explicitly */
+headerResults = [ "PASS", "NEW", "FAIL", "XFAIL", "CRASHED" ];
+logResults = [ "PASS", "NEW", "FAIL", "XFAIL", "CRASHED" ];
+resultToImgs = {
+ "PASS" : [],
+ "NEW" : [ "output" ],
+ "FAIL" : [ "output", "difference", "reference" ],
+ "XFAIL" : [],
+ "UNTESTED" : [],
+ "CRASHED" : []
+};
+
+resultToString = {
+ "PASS" : "",
+ "NEW" : "",
+ "FAIL" : "",
+ "XFAIL" : "",
+ "UNTESTED" : "",
+ "CRASHED" : "CRAHED!"
+};
+
+resultField = "result";
+rowFields = [ "test", "offset", "similar" ];
+colFields = [ "target", "format" ];
+allFields = [ resultField ].concat (rowFields, colFields);
+
+
+/* globals: */
+function resetGlobals () {
+ dragElement = undefined;
+ table = document.getElementById ("testTable");
+ while (table.rows.length)
+ table.deleteRow (0);
+ colsArray = [ "HrowHeader" ];
+ colsMap = undefined;
+ headerId = "HcolHeader";
+
+ fTrue = function (x) { return true; };
+
+ empty = new Row ();
+ header = new Row ();
+ header[colsArray[0]].toString = function () { return ""; };
+
+ untested = new Test ();
+ untested[resultField] = "UNTESTED";
+}
+
+
+/* utility functions */
+function normalizeKey (key) { return key.toLowerCase ().replace (/[^a-z0-9]/, ""); }
+function isVisible (x) { return x.style.display != "none"; }
+
+function link (html, url) { return "<a href='" + url + "'>" + html + "</a>"; }
+function image (url) { return "<img src='" + url + "'>"; }
+function span (html, id, cls) { return "<span id='" + id + "' class='" + cls + "' onmousedown='startDrag (event)' onmouseup='mouseUp (event)'>" + html + "</span>"; }
+
+function fieldsToHTML (bColumns, values) {
+ var fields = bColumns ? colFields : rowFields;
+ var prefix = bColumns ? "c" : "r";
+ var tmpRE = arrayApply (function (x) { return "[^/]*"; }, fields);
+ var r = Array ();
+ for (var i = 0; i < fields.length; i++)
+ if (fields[i] == "test") {
+ r.push (link (values[fields[i]], values[fields[i]] + ".log"));
+ } else {
+ tmpRE[i] = values[fields[i]];
+ r.push (span (values[fields[i]], prefix + "/" + tmpRE.join ("/") + "/", fields[i]));
+ tmpRE[i] = "[^/]*";
+ }
+ return r.join ("/");
+}
+
+function inArray (value, array) {
+ for (var i = 0; i < array.length; i++)
+ if (value == array[i])
+ return true;
+ return false;
+}
+
+function arrayApply (fun, array) {
+ var r = new Array ();
+ for (var i = 0; i < array.length; i++)
+ r.push (fun(array[i]));
+ return r;
+}
+
+function arrayPred (pred, array) {
+ var r = new Array ();
+ for (var i = 0; i < array.length; i++)
+ if (pred (array[i]))
+ r.push (array[i]);
+ return r;
+}
+
+function arrayMap (map, array) { return arrayApply (function (x) { return map[x]; }, array); }
+
+function binSearch (rows, newId){
+ var min = 0;
+ var max = rows.length;
+
+ while (max - min > 1) {
+ var mid = (max + min) >> 1;
+ if (rows[mid].id > newId)
+ max = mid;
+ else
+ min = mid;
+ }
+
+ if (max == min)
+ return max;
+ else
+ return rows[min].id > newId ? min : max;
+}
+
+/* dynamic table utils */
+function updateCurrent () {
+ for (var i = 0; i < table.rows.length; i++) {
+ var row = table.rows[i];
+ if (isVisible (row)) {
+ /* j starts from 1 because we want to ignore _rowHeader */
+ for (var j = 1; j < row.cells.length; j++)
+ if (row.id[0] == "H")
+ for (var k = 0; k < headerResults.length; k++)
+ header[row.cells[j].id].current[headerResults[k]] = 0;
+ else if (isVisible (row.cells[j]))
+ header[row.cells[j].id].current[row.cells[j].className]++;
+ }
+ }
+
+ updateHeader ();
+}
+
+function setVisible (array, subsetPred, visibilityPred, visibleFlag) {
+ var modified = false, somethingVisible = false;
+ for (var i = 0; i < array.length; i++)
+ if (array[i].id[0] != "H") {
+ if (subsetPred (array[i])) {
+ var wanted = visibilityPred (array[i]);
+ if (isVisible (array[i]) != wanted) {
+ modified = true;
+ array[i].style.display = wanted ? visibleFlag : "none";
+ }
+ }
+ somethingVisible = somethingVisible || isVisible (array[i]);
+ }
+ return modified && somethingVisible;
+}
+
+function setVisibleOnly (array, pred, visibleFlag) {
+ return setVisible (array, fTrue, pred, visibleFlag);
+}
+
+function flipVisible (array, subsetPred, visibleFlag) {
+ return setVisible (array, subsetPred, function (x) { return !isVisible (x); }, visibleFlag);
+}
+
+
+/* event handling */
+function ignoreEvent (event) {
+ if (event.preventDefault)
+ event.preventDefault();
+ else
+ event.returnValue= false;
+ return false;
+}
+
+function mouseUp (event) {
+ var visFun;
+ if (event.button == 0)
+ visFun = setVisibleOnly;
+ else if (event.button == 2)
+ visFun = flipVisible;
+ else
+ return false;
+
+ var structureFun;
+ if (event.target.id[0] == "r") /* rows */
+ structureFun = function (f, p) { return f (table.rows, p, "table-row"); };
+ else if (event.target.id[0] == "c") /* cols */
+ structureFun = function (f, p) { return inArray (true, arrayApply (function (row) { return f (row.cells, p, "table-cell") }, table.rows)) };
+ else
+ return false;
+
+ var pred;
+ if (event.target.id[1] == "/") { /* regexp */
+ var re = new RegExp (event.target.id);
+ pred = function (x) { return re.test (x.id); };
+ } else if (event.target.id[1] == "#") { /* counters */
+ var s = event.target.id.substr (2).split ("/");
+ pred = function (row) { return row.cells[s[0]].className == s[1]; }
+ } else
+ return false;
+
+ if (!structureFun (visFun, pred))
+ if (!structureFun (flipVisible, fTrue))
+ structureFun (flipVisible, fTrue);
+
+ updateCurrent ();
+
+ return false;
+}
+
+function noDrag (event) {
+ dragElement = undefined;
+ return false;
+}
+
+function startDrag (event) {
+ if (event.button == 0)
+ dragElement = event.target;
+ else
+ dragElement = undefined;
+ return false;
+}
+
+function endDrag (event) {
+ if (!dragElement)
+ return false;
+
+ if (event.currentTarget.id == colsArray[0] &&
+ inArray (dragElement.className, colFields)) {
+ rowFields.push (dragElement.className);
+ colFields = arrayPred (function (x) { return x != dragElement.className; }, colFields);
+ } else if (event.currentTarget.id == headerId &&
+ inArray (dragElement.className, rowFields)) {
+ colFields.push (dragElement.className);
+ rowFields = arrayPred (function (x) { return x != dragElement.className; }, rowFields);
+ } else
+ return true;
+
+ reloadAll ();
+ return false;
+}
+
+
+/* table content */
+function Row (id, t) {
+ this[colsArray[0]] = new RowHeader (id, t);
+
+ this.get = function (c) { return this[c] != undefined ? this[c] : untested; }
+ this.getHTML = function (c) { return this.get(c).toString (); };
+ this.setStyle = function (c, element) { return this.get(c).setStyle (element); };
+}
+
+function ColumnHeader (id, values) {
+ this.id = id;
+ this.values = values;
+ this.total = new Object ();
+ this.current = new Object ();
+
+ for (var i = 0; i < headerResults.length; i++) {
+ this.total[headerResults[i]] = 0;
+ this.current[headerResults[i]] = 0;
+ }
+
+ this.toString = function () {
+ var counts = new Array ();
+ for (var i = 0; i < headerResults.length; i++) {
+ var hr = headerResults[i];
+ var s = span (this.current[hr], "r#" + colsMap[this.id] + "/" + hr, hr);
+ if (this.current[hr] != this.total[hr])
+ s += span ("[" + this.total[hr] + "]", "r#" + colsMap[this.id] + "/" + hr, hr);
+ counts.push (s);
+ }
+
+ return fieldsToHTML (true, this.values) + "<br>" + counts.join ("/");
+ }
+
+ this.setStyle = function (element) { };
+}
+
+function RowHeader (id, values) {
+ this.id = id;
+ this.values = values;
+ this.toString = function () { return fieldsToHTML (false, this.values); }
+ this.setStyle = function (element) { element.onmouseup = endDrag; };
+}
+
+function Test () {
+ this.rowId = function () { return "r/" + arrayMap (this, rowFields).join("/") + "/"; };
+ this.colId = function () { return "c/" + arrayMap (this, colFields).join("/") + "/"; };
+ this.isComplete = function () { return !inArray (undefined, arrayMap (this, allFields)); }
+ this.toString = function () {
+ var images = arrayMap (this, resultToImgs[this[resultField]]);
+ images = arrayPred (function (x) { return x != undefined; }, images);
+ images = arrayApply (function (x) { return link (image (x), x); }, images);
+ images.push (resultToString[this[resultField]]);
+ return images.join (" ");
+ };
+
+ this.setStyle = function (element) { element.className = this[resultField]; };
+
+ this.addData = function (array) {
+ for (var i = 0; i < array.length - 1; i += 2)
+ this[normalizeKey (array[i])] = array[i+1];
+ };
+}
+
+
+/* table creation */
+function insertCell (domRow, nid, tests) {
+ var domCell = domRow.insertCell (nid);
+ domCell.id = colsArray[nid];
+ domCell.innerHTML = tests.getHTML (colsArray[nid]);
+ tests.setStyle (colsArray[nid], domCell);
+}
+
+function updateRow (row, tests) {
+ var domRow = document.getElementById (row);
+ if (!domRow) {
+ domRow = table.insertRow (binSearch (table.rows, row));
+ domRow.id = row;
+ }
+
+ for (var i = 0; i < colsArray.length; i++)
+ if (i >= domRow.cells.length || domRow.cells[i].id != colsArray[i])
+ insertCell (domRow, i, tests);
+}
+
+function updateHeader () {
+ var visibility;
+ var domRow = document.getElementById (headerId);
+ if (domRow) {
+ visibility = new Object ();
+ for (var i = 0; i < domRow.cells.length; i++)
+ visibility[domRow.cells[i].id] = domRow.cells[i].style.display;
+ table.deleteRow (domRow.rowIndex);
+ }
+
+ updateRow (headerId, header);
+ table.rows[0].onmouseup = endDrag;
+
+ if (visibility)
+ for (var i = 0; i < colsArray.length; i++)
+ if (visibility[colsArray[i]])
+ table.rows[0].cells[colsMap[colsArray[i]]].style.display = visibility[colsArray[i]];
+}
+
+function updateTable () {
+ colsArray.sort ();
+
+ colsMap = new Object ();
+ for (var i = 0; i < colsArray.length; i++)
+ colsMap[colsArray[i]] = i;
+
+ updateHeader ();
+ for (var i = 0; i < table.rows.length; i++)
+ updateRow (table.rows[i].id, empty);
+}
+
+
+/* log file parsing */
+function parseTest (testData) {
+ var colsChanged = false;
+ var rows = new Array ();
+ var data = new Object ();
+ var t = new Test ();
+ var lines = testData.split ("\n");
+ for (var i = 0; i < lines.length; i++) {
+ t.addData (lines[i].split (" "));
+ if (t.isComplete ()) {
+ var c = t.colId ();
+ if (header[c] == undefined) {
+ colsArray.push (c);
+ header[c] = new ColumnHeader (c, t);
+ colsChanged = true;
+ }
+
+ var r = t.rowId ();
+ if (!data[r]) {
+ rows.push (r);
+ data[r] = new Row (r, t);
+ }
+
+ data[r][c] = t;
+ header[c].total[t[resultField]]++;
+ header[c].current[t[resultField]]++;
+ t = new Test ();
+ }
+ }
+
+ if (colsChanged)
+ updateTable ();
+ else
+ updateHeader ();
+
+ for (var i = 0; i < rows.length; i++)
+ updateRow (rows[i], data[rows[i]]);
+}
+
+function parseFile (fileName, parser) {
+ var req = new XMLHttpRequest ();
+ req.onreadystatechange = function () {
+ if (req.readyState == 4)
+ parser (req.responseText);
+ }
+
+ try {
+ req.open ("GET", fileName);
+ req.send (null);
+ } catch (e) {}
+}
+
+function parseTestList (listData) {
+ var summaryRE = /\d+ Passed, \d+ Failed \x5b\d+ crashed, \d+ expected\x5d, \d+ Skipped/;
+ var lines = listData.split ("\n");
+ for (var i = 0; i < lines.length; i++) {
+ if (summaryRE.test (lines[i]))
+ return;
+
+ var words = lines[i].split (" ");
+ if (words.length >= 2 &&
+ words[0][words[0].length-1] == ":" &&
+ inArray (words[1], logResults))
+ parseFile (words[0].substr (0, words[0].length-1) + ".log", parseTest);
+ }
+}
+
+function reloadAll() {
+ resetGlobals ();
+
+ parseFile ("cairo-test-suite.log", parseTestList);
+}
+
+window.onload = reloadAll; \ No newline at end of file