testtable.js 12 KB

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