[cairo-commit] 3 commits - test/cairo-test-runner.c test/.gitignore test/index.html test/Makefile.am test/Makefile.win32 test/make-html.pl test/testtable.js

Andrea Canciani ranma42 at kemper.freedesktop.org
Thu Oct 7 14:20:58 PDT 2010


 test/.gitignore          |    1 
 test/Makefile.am         |   29 ---
 test/Makefile.win32      |   13 -
 test/cairo-test-runner.c |   13 +
 test/index.html          |   42 ++++
 test/make-html.pl        |  303 ---------------------------------
 test/testtable.js        |  426 +++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 490 insertions(+), 337 deletions(-)

New commits:
commit 425e68590b94d4701d81b73ef2232ea76dd97f48
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Sep 26 11:05:35 2010 +0200

    test: Remove old html infrastructure
    
    The new dynamic html page supersedes the old make-generated static
    html page.

diff --git a/test/.gitignore b/test/.gitignore
index b34bc64..400ce92 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -5,7 +5,6 @@ tags
 output
 Makefile
 Makefile.in
-index.html
 ref.hash
 any2ppm
 .any2ppm
diff --git a/test/Makefile.am b/test/Makefile.am
index a3c1f7a..6984ce1 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1413,14 +1413,15 @@ REFERENCE_IMAGES = \
 
 EXTRA_DIST +=		\
 6x13.pcf		\
-make-html.pl		\
+index.html		\
+jp2.jp2			\
 jpeg.jpg		\
 png.png			\
-jp2.jp2			\
 romedalen.jpg		\
 romedalen.png		\
 scarab.jpg		\
 surface-source.c	\
+testtable.js		\
 $(REFERENCE_IMAGES)
 
 # Any test for which the code committed to CVS is expected to fail
@@ -1655,7 +1656,6 @@ VALGRIND_FLAGS = \
 
 CLEANFILES +=					\
 	valgrind-log				\
-	index.html				\
 	ref.hash				\
 	png-test.png				\
 	png.out.png				\
@@ -1698,24 +1698,16 @@ recheck:
 	@echo Re-checking failed tests
 	@$(MAKE) $(AM_MAKEFLAGS) $(recheck)
 
-# Checks tests and creates index.html.
+# Checks tests.
 # Target doesn't fail if tests fail.
 test:
-	@$(MAKE) $(AM_MAKEFLAGS) check; \
-	$(MAKE) $(AM_MAKEFLAGS) html
+	@$(MAKE) $(AM_MAKEFLAGS) check
 
-# Re-checks tests and creates index.html.
+# Re-checks tests.
 # Target doesn't fail if tests fail.
 retest:
 	@CAIRO_TESTS="$(FAILED_TESTS)"; \
-	$(MAKE) $(AM_MAKEFLAGS) check; \
-	$(MAKE) $(AM_MAKEFLAGS) html
-
-html-local: index.html
-
-# Make index.html with no dependency tracking, containing only the failed tests.
-rehtml:
-	@CAIRO_TESTS="$(FAILED_TESTS)" $(MAKE) $(AM_MAKEFLAGS) html
+	$(MAKE) $(AM_MAKEFLAGS) check
 
 # Run tests under a tool specified by TOOL.  For example, make run TOOL=gdb
 run:
@@ -1733,11 +1725,6 @@ NOLOG_TESTS_LOG = $(NOLOG_TESTS:=.log)
 $(NOLOG_TESTS_LOG):
 	@echo dummy > $@
 
-index.html: $(srcdir)/make-html.pl
-	@echo Creating index.html
-	@perl $(srcdir)/make-html.pl > $@
-
-
 # Identify identical reference images
 check-ref-dups:
 	@LANG=C; \
@@ -1788,6 +1775,6 @@ check-ref-missing:
 
 release-verify-sane-tests: check-ref-missing
 
-.PHONY: check-valgrind test recheck retest rehtml check-ref-dups check-ref-missing release-verify-sane-tests
+.PHONY: check-valgrind test recheck retest check-ref-dups check-ref-missing release-verify-sane-tests
 
 EXTRA_DIST += Makefile.win32
diff --git a/test/Makefile.win32 b/test/Makefile.win32
index b45617b..8d8f1f8 100644
--- a/test/Makefile.win32
+++ b/test/Makefile.win32
@@ -33,20 +33,9 @@ cairo-test-suite.exe: $(OBJECTS) ./pdiff/pdiff.lib ../boilerplate/$(CFG)/boiler.
 ../boilerplate/$(CFG)/boiler.lib:
 	(cd ../boilerplate ; $(MAKE) -f Makefile.win32)
 
-.PHONY: check test html
+.PHONY: check test
 
 check: cairo-test-suite.exe
 	./cairo-test-suite.exe
 
-# define gen-html so that both 'test' and 'html' targets
-# can generate html while having different dependencies
-define gen-html
- at echo Creating index.html...
- at perl make-html.pl > index.html
-endef
-
 test: check
-	$(gen-html)
-
-html:
-	$(gen-html)
diff --git a/test/make-html.pl b/test/make-html.pl
deleted file mode 100755
index 9c0626d..0000000
--- a/test/make-html.pl
+++ /dev/null
@@ -1,303 +0,0 @@
-#!/usr/bin/perl
-#
-# Copyright © 2005 Mozilla Corporation
-#
-# Permission to use, copy, modify, distribute, and sell this software
-# and its documentation for any purpose is hereby granted without
-# fee, provided that the above copyright notice appear in all copies
-# and that both that copyright notice and this permission notice
-# appear in supporting documentation, and that the name of
-# Mozilla Corporation not be used in advertising or publicity pertaining to
-# distribution of the software without specific, written prior
-# permission. Mozilla Corporation makes no representations about the
-# suitability of this software for any purpose.  It is provided "as
-# is" without express or implied warranty.
-#
-# MOZILLA CORPORTAION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
-# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-# FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
-# INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
-# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
-# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#
-# Author: Vladimir Vukicevic <vladimir at pobox.com>
-#
-
-use MIME::Base64;
-
-##
-## Takes all the *.log files in the current directory (or those provided
-## on the command line) and spits out html to stdout that can be used to
-## view all the test results at once.
-##
-
-# show reference images
-$config_show_ref = $ENV{'CAIRO_TEST_SHOW_REF'} || 0;
-
-# show fail images
-$config_show_fail = $ENV{'CAIRO_TEST_SHOW_FAIL'} || 1;
-
-# show all results, even passes
-$config_show_all = $ENV{'CAIRO_TEST_SHOW_ALL'} || 0;
-
-# include test results inline
-$config_show_inline = $ENV{'CAIRO_TEST_SHOW_INLINE'} || 0;
-
-# end of config options
-
-$tests = {};
-$teststats = {};
-$logs = {};
-
-if ($#ARGV >= 0) { @files = @ARGV; } else { @files = <*.log>; }
-
-foreach (<@files>) {
-  my $testname;
-  my $out_path, $diff_path, $ref_path;
-  my $fn = $_;
-  (open LOG, $fn) || next;
-  while (<LOG>) {
-    if (/^OUTPUT: (.*)$/) {
-      $out_path = $1;
-      next;
-    }
-    if (/^DIFFERENCE: (.*)$/) {
-      $diff_path = $1;
-      next;
-    }
-    if (/^REFERENCE: (.*)$/) {
-      $ref_path = $1;
-      next;
-    }
-    next unless /^TEST: (.*) TARGET: (.*) FORMAT: (.*) OFFSET: (.*) SIMILAR: (.*) RESULT: ([A-Z]*).*$/;
-    $testname = $1 if !defined($testname);
-    $tests->{$1} = {} unless $tests->{$1};
-    $tests->{$1}->{$2} = {} unless $tests->{$1}->{$2};
-    $tests->{$1}->{$2}->{$3} = {} unless $tests->{$1}->{$2}->{$3};
-    $tests->{$1}->{$2}->{$3}->{$4} = {} unless $tests->{$1}->{$2}->{$3}->{$4};
-    $tests->{$1}->{$2}->{$3}->{$4}->{$5}->{'out'} = $out_path;
-    $tests->{$1}->{$2}->{$3}->{$4}->{$5}->{'diff'} = $diff_path;
-    $tests->{$1}->{$2}->{$3}->{$4}->{$5}->{'ref'} = $ref_path;
-    $tests->{$1}->{$2}->{$3}->{$4}->{$5}->{'result'} = $6;
-
-    $teststats->{$2} = {"PASS" => 0, "FAIL" => 0, "NEW" => 0, "XFAIL" => 0, "UNTESTED" => 0, "CRASHED" =>0}
-      unless $teststats->{$2};
-    ($teststats->{$2}->{$6})++;
-
-    undef $out_path;
-    undef $diff_path;
-    undef $ref_path;
-  }
-  close LOG;
-
-  (open LOG, $fn) || die "I could open it earlier, but I can't now: $!";
-  {
-    local $/;
-    $logs->{$testname} = <LOG>;
-  }
-  close LOG;
-}
-
-my $targeth = {};
-my $formath = {};
-my $offseth = {};
-my $similarh = {};
-
-foreach my $testname (sort(keys %$tests)) {
-  my $v0 = $tests->{$testname};
-  foreach my $targetname (sort(keys %$v0)) {
-    my $v1 = $v0->{$targetname};
-
-    $targeth->{$targetname} = 1;
-    foreach my $formatname (sort(keys %$v1)) {
-      my $v2 = $v1->{$formatname};
-
-      $formath->{$formatname} = 1;
-      foreach my $offsetval (sort(keys %$v2)) {
-        my $v3 = $v2->{$offsetval};
-
-        $offseth->{$offsetval} = 1;
-        foreach my $similarval (sort(keys %$v3)) {
-          $similarh->{$similarval} = 1;
-        }
-      }
-    }
-  }
-}
-
-my @targets = sort(keys %$targeth);
-my @formats = sort(keys %$formath);
-my @offsets = sort(keys %$offseth);
-my @similars = sort(keys %$similarh);
-
-sub printl {
-  print @_, "\n";
-}
-
-# convert file into a data URI
-sub file_to_data {
-  my ($ctype,$fname) = @_;
-  my $fdata;
-  open FILE, $fname || return "data:" . $ctype . ",";
-  {
-    local $/;
-    $fdata = encode_base64(<FILE>);
-  }
-  close FILE;
-  return "data:" . $ctype . ";base64," . $fdata;
-}
-
-# convert string into a data URI
-sub string_to_data {
-  my ($ctype,$str) = @_;
-  $str =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
-  return "data:" . $ctype . "," . $str;
-}
-
-printl '<html><head>';
-printl '<title>Cairo Test Results</title>';
-printl '<style type="text/css">';
-printl 'a img { border: solid 1px #FFF; }';
-printl '.PASS { background-color: #0B0; min-width: 1em; }';
-printl '.NEW { background-color: #B70; }';
-printl '.FAIL { background-color: #B00; }';
-printl '.XFAIL { background-color: #BB0; }';
-printl '.UNTESTED { background-color: #555; }';
-printl '.CRASHED { background-color: #F00; color: #FF0; }';
-printl '.PASSstr { color: #0B0; }';
-printl '.FAILstr { color: #D00; }';
-printl '.XFAILstr { color: #BB0; }';
-printl '.CRASHEDstr { color: #F00; }';
-printl '.UNTESTEDstr { color: #555; }';
-printl 'img { max-width: 15em; min-width: 3em; min-height: 3em; margin: 3px; }';
-printl 'td { vertical-align: top; }';
-printl '</style>';
-printl '</script>';
-printl '</head>';
-printl '<body>';
-
-printl '<table border="1">';
-print '<tr><th>Test</th>';
-
-foreach my $target (@targets) {
-  print '<th>', $target, '</th>';
-}
-printl '</tr>';
-
-print '<tr><td></td>';
-
-foreach my $target (@targets) {
-  print '<td>';
-  print '<span class="PASSstr">', $teststats->{$target}->{"PASS"}, '</span>/';
-  print '<span class="FAILstr">',
-	$teststats->{$target}->{"FAIL"} +
-	$teststats->{$target}->{"NEW"} +
-	$teststats->{$target}->{"CRASHED"},
-	'</span>/';
-  print '<span class="XFAILstr">', $teststats->{$target}->{"XFAIL"}, '</span>/';
-  print '<span class="UNTESTEDstr">', $teststats->{$target}->{"UNTESTED"}, '</span>';
-  print '</td>';
-}
-printl '</tr>';
-
-sub img_for {
-  my ($fn, $withlink) = @_;
-
-  return "" unless defined $fn;
-
-  if ($config_show_inline) {
-    $fn = file_to_data("image/png", $fn);
-    # never return links, people can just right-click view image,
-    # and we don't clutter the document
-    return '<img src="' . $fn . '">';
-  } else {
-    if ($withlink) {
-      return '<a href="' . $fn . '"><img src="' . $fn . '"></a>';
-    } else {
-      return '<img src="' . $fn . '">';
-    }
-  }
-}
-
-foreach my $test (sort(keys %$tests)) {
-  foreach my $offset (@offsets) {
-    foreach my $similar (@similars) {
-      foreach my $format (@formats) {
-        my $testline = "";
-
-        foreach my $target (@targets) {
-          my $tgtdata = $tests->{$test}->{$target};
-          if ($tgtdata) {
-            my $testres = $tgtdata->{$format}->{$offset}->{$similar};
-            if ($testres) {
-              my %testfiles;
-              $testfiles{'out'} = $testres->{'out'};
-              $testfiles{'diff'} = $testres->{'diff'};
-              $testfiles{'ref'} = $testres->{'ref'};
-
-              $testline .= "<td class=\"$testres->{'result'}\">";
-              $teststats{$target}{$testres}++;
-              if ($testres->{'result'} eq "PASS") {
-                if ($config_show_all) {
-                  $testline .= img_for($testfiles{'out'},1);
-                }
-              } elsif ($testres->{'result'} eq "FAIL") {
-                if ($config_show_fail || $config_show_all) {
-                  $testline .= img_for($testfiles{'out'},1);
-                  $testline .= " ";
-                  $testline .= img_for($testfiles{'diff'},1);
-                  $testline .= " ";
-                  $testline .= img_for($testfiles{'ref'},1);
-                }
-              } elsif ($testres->{'result'} eq "NEW") {
-                if ($config_show_fail || $config_show_all) {
-                  $testline .= img_for($testfiles{'new'},1);
-                }
-              } elsif ($testres->{'result'} eq "CRASHED") {
-                 $testline .= "!!!CRASHED!!!";
-              } elsif ($testres->{'result'} eq "XFAIL") {
-                #nothing
-                if ($config_show_all) {
-                  $testline .= img_for($testfiles{'out'},1);
-                  #$testline .= "<hr size=\"1\">";
-                  $testline .= " ";
-                  $testline .= img_for($testfiles{'diff'},1);
-                  $testline .= " ";
-                  $testline .= img_for($testfiles{'ref'},1);
-                }
-              } elsif ($testres->{'result'} eq "UNTESTED") {
-                #nothing
-              } else {
-                $testline .= "UNSUPPORTED STATUS '$testres->{'result'}' (update make-html.pl)";
-              }
-
-              $testline .= "</td>";
-            } else {
-              $testline .= '<td></td>';
-            }
-          } else {
-            $testline .= '<td></td>';
-          }
-        }
-        print '<tr><td>';
-
-        if ($config_show_inline) {
-	  print "$test ($format/$offset) ";
-	  print "(<a href=\"" . string_to_data("text/plain",$logs->{$test}) . "\">log</a>)";
-        } else {
-	  print $test, ' (', $format, '/', $offset, ($similar ? ' similar' : ''), ') ';
-	  print "(<a href=\"$test.log\">log</a>)";
-        }
-
-        print '</td>';
-
-        print $testline;
-
-        print "</tr>\n";
-      }
-    }
-  }
-}
-
-print "</table></body></html>\n";
commit f29e7d155f3a2ee75daf63719460348ea2ca0f76
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Aug 6 18:23:07 2010 +0200

    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.

diff --git a/test/index.html b/test/index.html
new file mode 100644
index 0000000..54c65cd
--- /dev/null
+++ b/test/index.html
@@ -0,0 +1,42 @@
+<html><head>
+<title>Cairo Test Results</title>
+<style type="text/css">
+#HcolHeader { text-align: center; }
+
+img { max-width: 15em; min-width: 3em; min-height: 3em; margin: 3px; }
+a img { border: solid 1px #FFF; }
+
+td { vertical-align: top; }
+span { cursor: pointer; }
+
+td.PASS { background-color: #0B0; min-width: 1em; }
+span.PASS { color: #0B0; }
+
+td.NEW { background-color: #B70; }
+span.NEW { color: #B70; }
+
+td.FAIL { background-color: #B00; }
+span.FAIL { color: #D00; }
+
+td.XFAIL { background-color: #BB0; }
+span.XFAIL { color: #BB0; }
+
+td.UNTESTED { background-color: #555; }
+span.UNTESTED { color: #555; }
+
+td.CRASHED { background-color: #F00; color: #FF0; }
+span.CRASHED { color: #F00; }
+
+.test { }
+.target { }
+.format { }
+.offset { }
+.similar { }
+
+</style>
+<script language="JavaScript" src="testtable.js"></script>
+</head>
+<body oncontextmenu='ignoreEvent (event)' onmouseup='noDrag (event)'>
+<table id="testTable" border="1"></table>
+</body>
+</html>
diff --git a/test/testtable.js b/test/testtable.js
new file mode 100644
index 0000000..0a6a6dd
--- /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
commit 4e064b3a32e4d699a6494bf9d8dbcd7b8d9cbc64
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Aug 6 18:20:49 2010 +0200

    test: Make tests run in natural order
    
    When tests are registered, they are prepended to a list.
    Reverting the list, we get a list whose order is the same as that
    of registration.

diff --git a/test/cairo-test-runner.c b/test/cairo-test-runner.c
index 5aea643..539817e 100644
--- a/test/cairo-test-runner.c
+++ b/test/cairo-test-runner.c
@@ -125,6 +125,18 @@ _log (cairo_test_context_t *ctx,
     va_end (ap);
 }
 
+static void
+_tests_reverse ()
+{
+    cairo_test_t *list, *next;
+
+    for (list = tests, tests = NULL; list != NULL; list = next) {
+	next = list->next;
+	list->next = tests;
+	tests = list;
+    }
+}
+
 static cairo_test_list_t *
 _list_prepend (cairo_test_list_t *head, const cairo_test_t *test)
 {
@@ -705,6 +717,7 @@ main (int argc, char **argv)
 #endif
 
     _cairo_test_runner_register_tests ();
+    _tests_reverse ();
 
     memset (&runner, 0, sizeof (runner));
     runner.num_device_offsets = 1;


More information about the cairo-commit mailing list