/* 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", "CRASH!" ]; resultToImgs = { "PASS" : [], "NEW" : [ "output" ], "FAIL" : [ "output", "difference", "reference" ], "XFAIL" : [], "UNTESTED" : [], "CRASHED" : [] }; resultToString = { "PASS" : "", "NEW" : "", "FAIL" : "", "XFAIL" : "", "UNTESTED" : "", "CRASHED" : "CRASHED!" }; resultField = "result"; rowFields = [ "test", "offset", "scale", "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 isKey (key) { return key[key.length-1] == ':'; } function normalizeKey (key) { return key.toLowerCase ().replace (/[^a-z0-9]/, ""); } function isVisible (x) { return x.style.display != "none"; } function link (html, url) { return "" + html + ""; } function image (url) { return ""; } function span (html, id, cls) { return "" + html + ""; } 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]], "output/" + 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) + "
" + 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) if (isKey (array[i])) 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.replace (/\r/g, "").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.replace (/\r/g, "").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 ("output/" + words[0].substr (0, words[0].length-1) + ".log", parseTest); } } function reloadAll() { resetGlobals (); parseFile ("cairo-test-suite.log", parseTestList); } window.onload = reloadAll;