summaryrefslogtreecommitdiffstats
path: root/WebKitTools/CSSTestSuiteHarness/harness
diff options
context:
space:
mode:
Diffstat (limited to 'WebKitTools/CSSTestSuiteHarness/harness')
-rw-r--r--WebKitTools/CSSTestSuiteHarness/harness/harness.css220
-rw-r--r--WebKitTools/CSSTestSuiteHarness/harness/harness.html206
-rw-r--r--WebKitTools/CSSTestSuiteHarness/harness/harness.js1314
3 files changed, 1740 insertions, 0 deletions
diff --git a/WebKitTools/CSSTestSuiteHarness/harness/harness.css b/WebKitTools/CSSTestSuiteHarness/harness/harness.css
new file mode 100644
index 0000000..8c0e7d1
--- /dev/null
+++ b/WebKitTools/CSSTestSuiteHarness/harness/harness.css
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+body {
+ font-family: Helvetica, sans-serif;
+ background-color: #DDD;
+}
+
+.controls {
+ border: 1px solid black;
+ width: 300px;
+ height: 660px;
+ float: left;
+ font-size: smaller;
+ padding: 4px;
+}
+
+.controls div {
+ margin: 4px;
+}
+
+.controls select {
+ width: 100%;
+}
+
+.details > div {
+ margin: 4px 0;
+}
+
+.actions {
+ margin-left: 320px;
+ border: 1px solid black;
+ font-size: smaller;
+ height: 30px;
+ padding: 4px;
+ margin-bottom: 8px;
+}
+
+.actions button {
+ font-size: 13px;
+ width: 5em;
+}
+
+.note {
+ font-size: 10px;
+ color: gray;
+}
+.action-buttons {
+ float: right;
+}
+
+#test-content {
+ margin-left: 320px;
+ height: 612px;
+ border: 1px solid black;
+ padding: 4px;
+ background-color: white;
+}
+
+.info > div {
+ margin: 6px 0;
+}
+
+.info .title {
+ font-size: larger;
+ font-weight: bold;
+}
+
+.info .url {
+ font-family: monospace;
+}
+
+.info .assertion, .info .flags {
+ font-size: smaller;
+}
+
+#test-content iframe {
+ border: 1px solid gray;
+ margin: 2px;
+}
+
+#test-content h2 {
+ font-size: 11pt;
+ margin: 2px 0 2px 0;
+ color: darkgray;
+}
+
+#test-list > option.untested {
+
+}
+
+#test-list > option.completed {
+ color: gray;
+}
+
+#test-content.with-ref {
+}
+
+.frame-wrapper {
+
+}
+
+.frame-wrapper iframe {
+ width: 98%;
+ height: 460px;
+}
+
+.frame-wrapper {
+ height: 500px;
+ width: 99%;
+ display: inline-block;
+}
+
+.with-ref > .frame-wrapper {
+ width: 49%;
+}
+
+#ref-wrapper {
+ height: 500px;
+ width: 49%;
+ display: none;
+}
+
+.with-ref > #ref-wrapper {
+ display: inline-block;
+}
+
+.results {
+ border: 1px solid black;
+ padding: 4px;
+ margin-top: 6px;
+}
+
+#output {
+ border: 1px solid black;
+ font-size: smaller;
+ height: 220px;
+ margin: 4px;
+ padding: 4px;
+ overflow-y: auto;
+ background-color: white;
+}
+
+#output > p {
+ margin: 0;
+}
+#output .pass {
+ color: green;
+}
+
+#output .fail {
+ color: red;
+}
+
+#output .skipped {
+ color: orange;
+}
+
+#output .invalid {
+ background: red;
+}
+
+.output-options {
+ float: right;
+ border: 1px solid black;
+ width: 200px;
+ height: 220px;
+ margin: 4px;
+ padding: 4px;
+ font-size: smaller;
+}
+
+.output-options select {
+ width: 90%;
+}
+
+.summary {
+ font-size: smaller;
+ margin: 4px;
+}
+
+.summary .label {
+ display: inline-block;
+ min-width: 5em;
+ margin: 0;
+}
+
+.summary span {
+ display: inline-block;
+ min-width: 3em;
+ text-align: right;
+ margin: 0;
+}
+
+.summary td {
+ text-align: right;
+ padding: 4px;
+}
diff --git a/WebKitTools/CSSTestSuiteHarness/harness/harness.html b/WebKitTools/CSSTestSuiteHarness/harness/harness.html
new file mode 100644
index 0000000..6bd47e5
--- /dev/null
+++ b/WebKitTools/CSSTestSuiteHarness/harness/harness.html
@@ -0,0 +1,206 @@
+<!--
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <title>CSS 2.1 Test Harness</title>
+ <link rel="stylesheet" href="harness.css" type="text/css" media="screen" charset="utf-8">
+
+ <script src="http://code.jquery.com/jquery-1.4.2.min.js" type="text/javascript" charset="utf-8"></script>
+ <script src="harness.js" type="text/javascript" charset="utf-8"></script>
+
+ <script type="text/javascript" charset="utf-8">
+ var gTestSuite;
+ function setupTests()
+ {
+ gTestSuite = new TestSuite();
+ }
+
+ window.addEventListener('load', setupTests, false);
+
+ function skipTest()
+ {
+ gTestSuite.skipTest(document.getElementById('skip-reason').value);
+ }
+
+ function invalidTest()
+ {
+ gTestSuite.invalidTest();
+ }
+
+ function failTest()
+ {
+ gTestSuite.failTest();
+ }
+
+ function passTest()
+ {
+ gTestSuite.passTest();
+ }
+
+ function formatChanged()
+ {
+ var newFormat;
+ if (document.harness.format.html4.checked)
+ newFormat = 'html4';
+ else
+ newFormat = 'xhtml1';
+ gTestSuite.formatChanged(newFormat);
+ }
+
+ function testSelected()
+ {
+ var list = document.getElementById('test-list')
+ if (list.selectedIndex >= 0)
+ gTestSuite.goToTestIndex(list.selectedIndex);
+ else
+ gTestSuite.clearTest();
+ }
+
+ function resultsPopupChanged(popup)
+ {
+ gTestSuite.resultsPopupChanged(popup.selectedIndex);
+ }
+
+ function doExport()
+ {
+ gTestSuite.exportResults(document.getElementById('results-popup').selectedIndex);
+ }
+
+ /*
+ This conflicts badly with key handling in selects.
+ function keyHandler(event)
+ {
+ var charCode = String.fromCharCode(event.keyCode);
+ window.console.log('keyHandler')
+ switch (charCode) {
+ case 'P':
+ passTest();
+ break;
+
+ case 'F':
+ failTest();
+ break;
+
+ case 'I':
+ invalidTest();
+ break;
+
+ case 'S':
+ skipTest();
+ break;
+ }
+
+ event.stopPropagation();
+ event.preventDefault();
+ }
+
+ document.addEventListener('keyup', keyHandler, false);
+ */
+ </script>
+
+</head>
+<body>
+
+ <div class="controls">
+ <form name="harness" onsubmit="return false;">
+ <select id="chapters">
+ <option>Test category</option>
+ </select>
+ <div class="progress">
+ <div><span id="test-index">1</span> of <span id="chapter-test-count">200</span> unique tests</div>
+ </div>
+ <div class="details">
+ <div class="name">
+ <div class="test-type">
+ <input type="radio" name="format" id="html4" onchange="formatChanged()" checked><label for="html4">HTML4</label><br>
+ <input type="radio" name="format" id="xhtml1" onchange="formatChanged()"><label for="xhtml1">XHTML1</label>
+ </div>
+ </div>
+ </div>
+
+ <div>
+ <select id="test-list" size="40" onchange="testSelected()"></select>
+ </div>
+ </form>
+ </div>
+
+ <div class="actions">
+ <span>Skip reason:</span> <input type="text" id="skip-reason" size="50">
+ <button onclick="skipTest()" accesskey="s">Skip</button>
+ <span class="note">Use <i>Control-Option-letter</i> to trigger buttons via the keyboard.</span>
+ <div class="action-buttons">
+ <button onclick="invalidTest()" accesskey="i">Invalid</button>
+ <button onclick="failTest()" accesskey="f">Fail</button>
+ <button onclick="passTest()" accesskey="p">Pass</button>
+ </div>
+ </div>
+ <div id="test-content">
+ <div class="info">
+ <div class="title">Title: <span id="test-title"></span></div>
+ <div class="url">URL: <span id="test-url"></span></div>
+ <div class="assertion">Assertion: <span id="test-assertion"></span></div>
+ <div class="flags">Flags: <span id="test-flags"></span></div>
+ </div>
+
+ <div id="test-wrapper" class="frame-wrapper">
+ <h2>Test</h2>
+ <iframe id="test-frame"></iframe>
+ </div>
+ <div id="ref-wrapper" class="frame-wrapper">
+ <h2>Reference</h2>
+ <iframe id="ref-frame"></iframe>
+ </div>
+ </div>
+
+ <div class="results">
+ <div class="output-options">
+ <p>Show results for:</p>
+ <select id="results-popup" onchange="resultsPopupChanged(this)">
+ </select>
+ <button id="export-button" onclick="doExport()">Export...</button>
+ </div>
+ <div id="output"></div>
+ <div class="summary">
+ <table>
+ <tr>
+ <th></th><th>Passed</th><th>Failed</th><th>Skipped</th><th>Invalid</th><th>Tested</th><th>Total</th><th>% done</th>
+ </tr>
+ <tr>
+ <td class="label">HTML4:</td><td id="h-passed"></td><td id="h-failed"></td><td id="h-skipped"></td><td id="h-invalid"></td><td id="h-tested"></td><td id="h-total"></td><td id="h-percent"></td>
+ </tr>
+ <tr>
+ <td class="label">XHTML1:</td><td id="x-passed"></td><td id="x-failed"></td><td id="x-skipped"></td><td id="x-invalid"></td><td id="x-tested"></td><td id="x-total"></td><td id="x-percent"></td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/WebKitTools/CSSTestSuiteHarness/harness/harness.js b/WebKitTools/CSSTestSuiteHarness/harness/harness.js
new file mode 100644
index 0000000..95262af
--- /dev/null
+++ b/WebKitTools/CSSTestSuiteHarness/harness/harness.js
@@ -0,0 +1,1314 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// requires jQuery
+
+const kTestSuiteVersion = '20100917';
+const kTestSuiteHome = '../' + kTestSuiteVersion + '/';
+const kTestInfoDataFile = 'testinfo.data';
+
+const kChapterData = [
+ {
+ 'file' : 'about.html',
+ 'title' : 'About the CSS 2.1 Specification',
+ },
+ {
+ 'file' : 'intro.html',
+ 'title' : 'Introduction to CSS 2.1',
+ },
+ {
+ 'file' : 'conform.html',
+ 'title' : 'Conformance: Requirements and Recommendations',
+ },
+ {
+ 'file' : "syndata.html",
+ 'title' : 'Syntax and basic data types',
+ },
+ {
+ 'file' : 'selector.html' ,
+ 'title' : 'Selectors',
+ },
+ {
+ 'file' : 'cascade.html',
+ 'title' : 'Assigning property values, Cascading, and Inheritance',
+ },
+ {
+ 'file' : 'media.html',
+ 'title' : 'Media types',
+ },
+ {
+ 'file' : 'box.html' ,
+ 'title' : 'Box model',
+ },
+ {
+ 'file' : 'visuren.html',
+ 'title' : 'Visual formatting model',
+ },
+ {
+ 'file' :'visudet.html',
+ 'title' : 'Visual formatting model details',
+ },
+ {
+ 'file' : 'visufx.html',
+ 'title' : 'Visual effects',
+ },
+ {
+ 'file' : 'generate.html',
+ 'title' : 'Generated content, automatic numbering, and lists',
+ },
+ {
+ 'file' : 'page.html',
+ 'title' : 'Paged media',
+ },
+ {
+ 'file' : 'colors.html',
+ 'title' : 'Colors and Backgrounds',
+ },
+ {
+ 'file' : 'fonts.html',
+ 'title' : 'Fonts',
+ },
+ {
+ 'file' : 'text.html',
+ 'title' : 'Text',
+ },
+ {
+ 'file' : 'tables.html',
+ 'title' : 'Tables',
+ },
+ {
+ 'file' : 'ui.html',
+ 'title' : 'User interface',
+ },
+ {
+ 'file' : 'aural.html',
+ 'title' : 'Appendix A. Aural style sheets',
+ },
+ {
+ 'file' : 'refs.html',
+ 'title' : 'Appendix B. Bibliography',
+ },
+ {
+ 'file' : 'changes.html',
+ 'title' : 'Appendix C. Changes',
+ },
+ {
+ 'file' : 'sample.html',
+ 'title' : 'Appendix D. Default style sheet for HTML 4',
+ },
+ {
+ 'file' : 'zindex.html',
+ 'title' : 'Appendix E. Elaborate description of Stacking Contexts',
+ },
+ {
+ 'file' : 'propidx.html',
+ 'title' : 'Appendix F. Full property table',
+ },
+ {
+ 'file' : 'grammar.html',
+ 'title' : 'Appendix G. Grammar of CSS',
+ },
+ {
+ 'file' : 'other.html',
+ 'title' : 'Other',
+ },
+];
+
+
+const kHTML4Data = {
+ 'path' : 'html4',
+ 'suffix' : '.htm'
+};
+
+const kXHTML1Data = {
+ 'path' : 'xhtml1',
+ 'suffix' : '.xht'
+};
+
+// Results popup
+const kResultsSelector = [
+ {
+ 'name': 'All Tests',
+ 'handler' : function(self) { self.showResultsForAllTests(); },
+ 'exporter' : function(self) { self.exportResultsForAllTests(); }
+ },
+ {
+ 'name': 'Completed Tests',
+ 'handler' : function(self) { self.showResultsForCompletedTests(); },
+ 'exporter' : function(self) { self.exportResultsForCompletedTests(); }
+ },
+ {
+ 'name': 'Passing Tests',
+ 'handler' : function(self) { self.showResultsForTestsWithStatus('pass'); },
+ 'exporter' : function(self) { self.exportResultsForTestsWithStatus('pass'); }
+ },
+ {
+ 'name': 'Failing Tests',
+ 'handler' : function(self) { self.showResultsForTestsWithStatus('fail'); },
+ 'exporter' : function(self) { self.exportResultsForTestsWithStatus('fail'); }
+ },
+ {
+ 'name': 'Skipped Tests',
+ 'handler' : function(self) { self.showResultsForTestsWithStatus('skipped'); },
+ 'exporter' : function(self) { self.exportResultsForTestsWithStatus('skipped'); }
+ },
+ {
+ 'name': 'Invalid Tests',
+ 'handler' : function(self) { self.showResultsForTestsWithStatus('invalid'); },
+ 'exporter' : function(self) { self.exportResultsForTestsWithStatus('invalid'); }
+ },
+ {
+ 'name': 'Tests where HTML4 and XHTML1 results differ',
+ 'handler' : function(self) { self.showResultsForTestsWithMismatchedResults(); },
+ 'exporter' : function(self) { self.exportResultsForTestsWithMismatchedResults(); }
+ },
+ {
+ 'name': 'Tests Not Run',
+ 'handler' : function(self) { self.showResultsForTestsNotRun(); },
+ 'exporter' : function(self) { self.exportResultsForTestsNotRun(); }
+ }
+];
+
+function Test(testInfoLine)
+{
+ var fields = testInfoLine.split('\t');
+
+ this.id = fields[0];
+ this.reference = fields[1];
+ this.title = fields[2];
+ this.flags = fields[3];
+ this.links = fields[4];
+ this.assertion = fields[5];
+
+ this.completed = false; // true if this test has a result (pass, fail or skip)
+
+ if (!this.links)
+ this.links = "other.html"
+}
+
+function ChapterSection(link)
+{
+ var result= link.match(/^([.\w]+)(#.+)?$/);
+ if (result != null) {
+ this.file = result[1];
+ this.anchor = result[2];
+ }
+
+ this.tests = [];
+}
+
+function Chapter(chapterInfo)
+{
+ this.file = chapterInfo.file;
+ this.title = chapterInfo.title;
+ this.testCount = 0;
+ this.sections = []; // array of ChapterSection
+}
+
+Chapter.prototype.description = function()
+{
+ return this.title + ' (' + this.testCount + ' tests)';
+}
+
+// Utils
+String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }
+
+function TestSuite()
+{
+ this.chapterSections = {}; // map of links to ChapterSections
+ this.tests = {}; // map of test id to test info
+
+ this.chapters = {}; // map of file name to chapter
+ this.currentChapter = null;
+
+ this.currentChapterTests = []; // array of tests for the current chapter.
+ this.currChapterTestIndex = -1; // index of test in the current chapter
+
+ this.format = '';
+ this.formatChanged('html4');
+
+ this.testInfoLoaded = false;
+
+ this.populatingDatabase = false;
+
+ var testInfoPath = kTestSuiteHome + kTestInfoDataFile;
+ this.loadTestInfo(testInfoPath);
+}
+
+TestSuite.prototype.loadTestInfo = function(testInfoPath)
+{
+ var _self = this;
+ this.asyncLoad(testInfoPath, 'data', function(data, status) {
+ _self.testInfoDataLoaded(data, status);
+ });
+}
+
+TestSuite.prototype.testInfoDataLoaded = function(data, status)
+{
+ if (status != 'success') {
+ alert("Failed to load testinfo.data. Database of tests will not be initialized.");
+ return;
+ }
+
+ this.parseTests(data);
+ this.buildChapters();
+
+ this.testInfoLoaded = true;
+
+ this.fillChapterPopup(document.getElementById('chapters'));
+
+ this.initializeControls();
+
+ this.openDatabase();
+}
+
+TestSuite.prototype.parseTests = function(data)
+{
+ var lines = data.split('\n');
+
+ // First line is column labels
+ for (var i = 1; i < lines.length; ++i) {
+ var test = new Test(lines[i]);
+ if (test.id.length > 0)
+ this.tests[test.id] = test;
+ }
+}
+
+TestSuite.prototype.buildChapters = function()
+{
+ for (var testID in this.tests) {
+ var currTest = this.tests[testID];
+
+ // FIXME: tests with more than one link will be presented to the user
+ // twice. Be smarter about avoiding this.
+ var testLinks = currTest.links.split(',');
+ for (var i = 0; i < testLinks.length; ++i) {
+ var link = testLinks[i];
+ var section = this.chapterSections[link];
+ if (!section) {
+ section = new ChapterSection(link);
+ this.chapterSections[link] = section;
+ }
+
+ section.tests.push(currTest);
+ }
+ }
+
+ for (var i = 0; i < kChapterData.length; ++i) {
+ var chapter = new Chapter(kChapterData[i]);
+ chapter.index = i;
+ this.chapters[chapter.file] = chapter;
+ }
+
+ for (var sectionName in this.chapterSections) {
+ var section = this.chapterSections[sectionName];
+
+ var file = section.file;
+ var chapter = this.chapters[file];
+ if (!chapter)
+ window.console.log('failed to find chapter ' + file + ' in chapter data.');
+ chapter.sections.push(section);
+ }
+
+ for (var chapterName in this.chapters) {
+ var currChapter = this.chapters[chapterName];
+ currChapter.sections.sort();
+
+ var testCount = 0;
+ for (var s = 0; s < currChapter.sections.length; ++s)
+ testCount += currChapter.sections[s].tests.length;
+
+ currChapter.testCount = testCount;
+ }
+}
+
+TestSuite.prototype.indexOfChapter = function(chapter)
+{
+ for (var i = 0; i < kChapterData.length; ++i) {
+ if (kChapterData[i].file == chapter.file)
+ return i;
+ }
+
+ window.console.log('indexOfChapter for ' + chapter.file + ' failed');
+ return -1;
+}
+
+TestSuite.prototype.chapterAtIndex = function(index)
+{
+ if (index < 0 || index >= kChapterData.length)
+ return null;
+
+ return this.chapters[kChapterData[index].file];
+}
+
+TestSuite.prototype.fillChapterPopup = function(select)
+{
+ select.innerHTML = ''; // Remove all children.
+
+ for (var i = 0; i < kChapterData.length; ++i) {
+ var chapterData = kChapterData[i];
+ var chapter = this.chapters[chapterData.file];
+
+ var option = document.createElement('option');
+ option.innerText = chapter.description();
+ option._chapter = chapter;
+
+ select.appendChild(option);
+ }
+}
+
+TestSuite.prototype.buildTestListForChapter = function(chapter)
+{
+ this.currentChapterTests = [];
+
+ for (var i in chapter.sections) {
+ var currSection = chapter.sections[i];
+ // FIXME: why do I need the assignment?
+ this.currentChapterTests = this.currentChapterTests.concat(currSection.tests);
+ }
+
+ // FIXME: test may occur more than once.
+ this.currentChapterTests.sort(function(a, b) {
+ return a.id.localeCompare(b.id);
+ });
+}
+
+TestSuite.prototype.initializeControls = function()
+{
+ var chaptersPopup = document.getElementById('chapters');
+
+ var _self = this;
+ chaptersPopup.addEventListener('change', function() {
+ _self.chapterPopupChanged();
+ }, false);
+
+ this.chapterPopupChanged();
+
+ // Results popup
+ var resultsPopup = document.getElementById('results-popup');
+ resultsPopup.innerHTML = '';
+
+ for (var i = 0; i < kResultsSelector.length; ++i) {
+ var option = document.createElement('option');
+ option.innerText = kResultsSelector[i].name;
+
+ resultsPopup.appendChild(option);
+ }
+}
+
+TestSuite.prototype.chapterPopupChanged = function()
+{
+ var chaptersPopup = document.getElementById('chapters');
+ var selectedChapter = chaptersPopup.options[chaptersPopup.selectedIndex]._chapter;
+
+ this.setSelectedChapter(selectedChapter);
+}
+
+TestSuite.prototype.fillTestList = function()
+{
+ var testList = document.getElementById('test-list');
+ testList.innerHTML = '';
+
+ for (var i = 0; i < this.currentChapterTests.length; ++i) {
+ var currTest = this.currentChapterTests[i];
+
+ var option = document.createElement('option');
+ option.innerText = currTest.id;
+ option.className = currTest.completed ? 'completed' : 'untested';
+ option._test = currTest;
+ testList.appendChild(option);
+ }
+}
+
+TestSuite.prototype.updateTestList = function()
+{
+ var testList = document.getElementById('test-list');
+
+ var options = testList.getElementsByTagName('option');
+ for (var i = 0; i < options.length; ++i) {
+ var currOption = options[i];
+ currOption.className = currOption._test.completed ? 'completed' : 'untested';
+ }
+}
+
+TestSuite.prototype.setSelectedChapter = function(chapter)
+{
+ this.currentChapter = chapter;
+ this.buildTestListForChapter(this.currentChapter);
+ this.currChapterTestIndex = -1;
+
+ this.fillTestList();
+ this.goToTestIndex(0);
+
+ var chaptersPopup = document.getElementById('chapters');
+ chaptersPopup.selectedIndex = this.indexOfChapter(chapter);
+}
+
+/* ------------------------------------------------------- */
+
+TestSuite.prototype.passTest = function()
+{
+ this.recordResult(this.currentTestName(), 'pass');
+ this.nextTest();
+}
+
+TestSuite.prototype.failTest = function()
+{
+ this.recordResult(this.currentTestName(), 'fail');
+ this.nextTest();
+}
+
+TestSuite.prototype.invalidTest = function()
+{
+ this.recordResult(this.currentTestName(), 'invalid');
+ this.nextTest();
+}
+
+TestSuite.prototype.skipTest = function(reason)
+{
+ this.recordResult(this.currentTestName(), 'skipped', reason);
+ this.nextTest();
+}
+
+TestSuite.prototype.nextTest = function()
+{
+ if (this.currChapterTestIndex < this.currentChapterTests.length - 1)
+ this.goToTestIndex(this.currChapterTestIndex + 1);
+ else {
+ var currChapterIndex = this.indexOfChapter(this.currentChapter);
+ this.goToChapterIndex(currChapterIndex + 1);
+ }
+}
+
+TestSuite.prototype.previousTest = function()
+{
+ if (this.currChapterTestIndex > 0)
+ this.goToTestIndex(this.currChapterTestIndex - 1);
+ else {
+ var currChapterIndex = this.indexOfChapter(this.currentChapter);
+ if (currChapterIndex > 0)
+ this.goToChapterIndex(currChapterIndex - 1);
+ }
+}
+
+/* ------------------------------------------------------- */
+
+TestSuite.prototype.goToTestIndex = function(index)
+{
+ if (index >= 0 && index < this.currentChapterTests.length) {
+ this.currChapterTestIndex = index;
+ this.loadCurrentTest();
+ }
+}
+
+TestSuite.prototype.goToChapterIndex = function(chapterIndex)
+{
+ if (chapterIndex >= 0 && chapterIndex < kChapterData.length) {
+ var chapterFile = kChapterData[chapterIndex].file;
+ this.setSelectedChapter(this.chapters[chapterFile]);
+ }
+}
+
+TestSuite.prototype.currentTestName = function()
+{
+ if (this.currChapterTestIndex < 0 || this.currChapterTestIndex >= this.currentChapterTests.length)
+ return undefined;
+
+ return this.currentChapterTests[this.currChapterTestIndex].id;
+}
+
+TestSuite.prototype.loadCurrentTest = function()
+{
+ var theTest = this.currentChapterTests[this.currChapterTestIndex];
+ if (!theTest) {
+ this.configureForManualTest();
+ this.clearTest();
+ return;
+ }
+
+ if (theTest.reference) {
+ this.configureForRefTest();
+ this.loadRef(theTest);
+ } else {
+ this.configureForManualTest();
+ }
+
+ this.loadTest(theTest);
+
+ document.getElementById('test-index').innerText = this.currChapterTestIndex + 1;
+ document.getElementById('chapter-test-count').innerText = this.currentChapterTests.length;
+
+ document.getElementById('test-list').selectedIndex = this.currChapterTestIndex;
+}
+
+TestSuite.prototype.configureForRefTest = function()
+{
+ $('#test-content').addClass('with-ref');
+}
+
+TestSuite.prototype.configureForManualTest = function()
+{
+ $('#test-content').removeClass('with-ref');
+}
+
+TestSuite.prototype.loadTest = function(test)
+{
+ var iframe = document.getElementById('test-frame');
+ iframe.src = this.urlForTest(test.id);
+
+ document.getElementById('test-title').innerText = test.title;
+ document.getElementById('test-url').innerText = this.pathForTest(test.id);
+ document.getElementById('test-assertion').innerText = test.assertion;
+ document.getElementById('test-flags').innerText = test.flags;
+}
+
+TestSuite.prototype.clearTest = function()
+{
+ var iframe = document.getElementById('test-frame');
+ iframe.src = 'about:blank';
+
+ document.getElementById('test-title').innerText = '';
+ document.getElementById('test-url').innerText = '';
+ document.getElementById('test-assertion').innerText = '';
+ document.getElementById('test-flags').innerText = '';
+}
+
+TestSuite.prototype.loadRef = function(test)
+{
+ var iframe = document.getElementById('ref-frame');
+ iframe.src = this.urlForTest(testInfo.reference);
+}
+
+TestSuite.prototype.loadTestByName = function(testName)
+{
+ var currChapterInfo = this.chapterInfoMap[this.currChapterName];
+
+ var testIndex = currChapterInfo.testNames.indexOf(testName);
+ if (testIndex >= 0 && testIndex < currChapterInfo.testNames.length)
+ this.goToTestIndex(testIndex);
+}
+
+TestSuite.prototype.pathForTest = function(testName)
+{
+ var prefix = this.formatInfo.path;
+ var suffix = this.formatInfo.suffix;
+
+ return prefix + '/' + testName + suffix;
+}
+
+TestSuite.prototype.urlForTest = function(testName)
+{
+ return kTestSuiteHome + this.pathForTest(testName);
+}
+
+/* ------------------------------------------------------- */
+
+TestSuite.prototype.recordResult = function(testName, resolution, comment)
+{
+ if (!testName)
+ return;
+
+ this.beginAppendingOutput();
+ this.appendResultToOutput(this.formatInfo, testName, resolution, comment);
+ this.endAppendingOutput();
+
+ if (comment == undefined)
+ comment = '';
+
+ this.storeTestResult(testName, this.format, resolution, comment, navigator.userAgent);
+ this.markTestCompleted(testName);
+ this.updateTestList();
+
+ this.updateSummaryData();
+}
+
+TestSuite.prototype.beginAppendingOutput = function()
+{
+}
+
+TestSuite.prototype.endAppendingOutput = function()
+{
+ var output = document.getElementById('output');
+ output.scrollTop = output.scrollHeight;
+}
+
+TestSuite.prototype.appendResultToOutput = function(formatData, testName, resolution, comment)
+{
+ var output = document.getElementById('output');
+
+ var result = formatData.path + '/' + testName + formatData.suffix + '\t' + resolution;
+ if (comment)
+ result += '\t(' + comment + ')';
+
+ var line = document.createElement('p');
+ line.className = resolution;
+ line.appendChild(document.createTextNode(result));
+ output.appendChild(line);
+}
+
+TestSuite.prototype.clearOutput = function()
+{
+ document.getElementById('output').innerHTML = '';
+}
+
+/* ------------------------------------------------------- */
+
+TestSuite.prototype.formatChanged = function(formatString)
+{
+ if (this.format == formatString)
+ return;
+
+ this.format = formatString;
+
+ if (formatString == 'html4')
+ this.formatInfo = kHTML4Data;
+ else
+ this.formatInfo = kXHTML1Data;
+
+ this.loadCurrentTest();
+}
+
+/* ------------------------------------------------------- */
+
+TestSuite.prototype.asyncLoad = function(url, type, handler)
+{
+ $.get(url, handler, type);
+}
+
+/* ------------------------------------------------------- */
+
+TestSuite.prototype.exportResults = function(resultTypeIndex)
+{
+ var resultInfo = kResultsSelector[resultTypeIndex];
+ if (!resultInfo)
+ return;
+
+ resultInfo.exporter(this);
+}
+
+TestSuite.prototype.exportHeader = function()
+{
+ var result = '# Safari 5.0.2' + ' ' + navigator.platform + '\n';
+ result += '# ' + navigator.userAgent + '\n';
+ result += '# http://test.csswg.org/suites/css2.1/' + kTestSuiteVersion + '/\n';
+ result += 'testname\tresult\n';
+
+ return result;
+}
+
+TestSuite.prototype.createExportLine = function(formatData, testName, resolution, comment)
+{
+ var result = formatData.path + '/' + testName + '\t' + resolution;
+ if (comment)
+ result += '\t(' + comment + ')';
+ return result;
+}
+
+TestSuite.prototype.exportQueryComplete = function(data)
+{
+ window.open("data:text/plain," + escape(data))
+}
+
+TestSuite.prototype.resultsPopupChanged = function(index)
+{
+ var resultInfo = kResultsSelector[index];
+ if (!resultInfo)
+ return;
+
+ this.clearOutput();
+ resultInfo.handler(this);
+
+ var enableExport = resultInfo.exporter != undefined;
+ document.getElementById('export-button').disabled = !enableExport;
+}
+
+/* -------------------------------------------------------- */
+
+TestSuite.prototype.exportResultsCompletion = function(exportTests)
+{
+ window.console.log('exportResultsCompletion')
+ // Lame workaround for ORDER BY not working
+ exportTests.sort(function(a, b) {
+ return a.test.localeCompare(b.test);
+ });
+
+ var exportLines = [];
+ for (var i = 0; i < exportTests.length; ++i) {
+ var currTest = exportTests[i];
+ if (currTest.html4 != '')
+ exportLines.push(currTest.html4);
+ if (currTest.xhtml1 != '')
+ exportLines.push(currTest.xhtml1);
+ }
+
+ var exportString = this.exportHeader() + exportLines.join('\n');
+ this.exportQueryComplete(exportString);
+}
+
+/* -------------------------------------------------------- */
+
+TestSuite.prototype.showResultsForCompletedTests = function()
+{
+ this.beginAppendingOutput();
+
+ var _self = this;
+ this.queryDatabaseForCompletedTests(
+ function(item) {
+ if (item.hstatus)
+ _self.appendResultToOutput(kHTML4Data, item.test, item.hstatus, item.hcomment);
+
+ if (item.xstatus)
+ _self.appendResultToOutput(kXHTML1Data, item.test, item.xstatus, item.xcomment);
+ },
+ function() {
+ _self.endAppendingOutput();
+ }
+ );
+}
+
+TestSuite.prototype.exportResultsForCompletedTests = function()
+{
+ var exportTests = []; // each test will have html and xhtml items on it
+
+ var _self = this;
+ this.queryDatabaseForCompletedTests(
+ function(item) {
+ var htmlLine = '';
+ if (item.hstatus)
+ htmlLine= _self.createExportLine(kHTML4Data, item.test, item.hstatus, item.hcomment);
+
+ var xhtmlLine = '';
+ if (item.xstatus)
+ xhtmlLine = _self.createExportLine(kXHTML1Data, item.test, item.xstatus, item.xcomment);
+
+ exportTests.push({
+ 'test' : item.test,
+ 'html4' : htmlLine,
+ 'xhtml1' : xhtmlLine });
+ },
+ function() {
+ _self.exportResultsCompletion(exportTests);
+ }
+ );
+}
+
+
+/* -------------------------------------------------------- */
+
+TestSuite.prototype.showResultsForAllTests = function()
+{
+ this.beginAppendingOutput();
+
+ var _self = this;
+ this.queryDatabaseForAllTests('test',
+ function(item) {
+ _self.appendResultToOutput(kHTML4Data, item.test, item.hstatus, item.hcomment);
+ _self.appendResultToOutput(kXHTML1Data, item.test, item.xstatus, item.xcomment);
+ },
+ function() {
+ _self.endAppendingOutput();
+ });
+}
+
+TestSuite.prototype.exportResultsForAllTests = function()
+{
+ var exportTests = [];
+
+ var _self = this;
+ this.queryDatabaseForAllTests('test',
+ function(item) {
+ var htmlLine= _self.createExportLine(kHTML4Data, item.test, item.hstatus, item.hcomment);
+ var xhtmlLine = _self.createExportLine(kXHTML1Data, item.test, item.xstatus, item.xcomment);
+ exportTests.push({
+ 'test' : item.test,
+ 'html4' : htmlLine,
+ 'xhtml1' : xhtmlLine });
+ },
+ function() {
+ _self.exportResultsCompletion(exportTests);
+ }
+ );
+}
+
+/* -------------------------------------------------------- */
+
+TestSuite.prototype.showResultsForTestsNotRun = function()
+{
+ this.beginAppendingOutput();
+
+ var _self = this;
+ this.queryDatabaseForTestsNotRun(
+ function(item) {
+ if (!item.hstatus)
+ _self.appendResultToOutput(kHTML4Data, item.test, '?', item.hcomment);
+ if (!item.xstatus)
+ _self.appendResultToOutput(kXHTML1Data, item.test, '?', item.xcomment);
+ },
+ function() {
+ _self.endAppendingOutput();
+ }
+ );
+}
+
+TestSuite.prototype.exportResultsForTestsNotRun = function()
+{
+ var exportTests = [];
+
+ var _self = this;
+ this.queryDatabaseForTestsNotRun(
+ function(item) {
+ var htmlLine = '';
+ if (!item.hstatus)
+ htmlLine= _self.createExportLine(kHTML4Data, item.test, '?', item.hcomment);
+
+ var xhtmlLine = '';
+ if (!item.xstatus)
+ xhtmlLine = _self.createExportLine(kXHTML1Data, item.test, '?', item.xcomment);
+
+ exportTests.push({
+ 'test' : item.test,
+ 'html4' : htmlLine,
+ 'xhtml1' : xhtmlLine });
+ },
+ function() {
+ _self.exportResultsCompletion(exportTests);
+ }
+ );
+}
+
+/* -------------------------------------------------------- */
+
+TestSuite.prototype.showResultsForTestsWithStatus = function(status)
+{
+ this.beginAppendingOutput();
+
+ var _self = this;
+ this.queryDatabaseForTestsWithStatus(status,
+ function(item) {
+ if (item.hstatus == status)
+ _self.appendResultToOutput(kHTML4Data, item.test, item.hstatus, item.hcomment);
+ if (item.xstatus == status)
+ _self.appendResultToOutput(kXHTML1Data, item.test, item.xstatus, item.xcomment);
+ },
+ function() {
+ _self.endAppendingOutput();
+ }
+ );
+}
+
+TestSuite.prototype.exportResultsForTestsWithStatus = function(status)
+{
+ var exportTests = [];
+
+ var _self = this;
+ this.queryDatabaseForTestsWithStatus(status,
+ function(item) {
+ var htmlLine = '';
+ if (item.hstatus == status)
+ htmlLine= _self.createExportLine(kHTML4Data, item.test, item.hstatus, item.hcomment);
+
+ var xhtmlLine = '';
+ if (item.xstatus == status)
+ xhtmlLine = _self.createExportLine(kXHTML1Data, item.test, item.xstatus, item.xcomment);
+
+ exportTests.push({
+ 'test' : item.test,
+ 'html4' : htmlLine,
+ 'xhtml1' : xhtmlLine });
+ },
+ function() {
+ _self.exportResultsCompletion(exportTests);
+ }
+ );
+}
+
+/* -------------------------------------------------------- */
+
+TestSuite.prototype.showResultsForTestsWithMismatchedResults = function()
+{
+ this.beginAppendingOutput();
+
+ var _self = this;
+ this.queryDatabaseForTestsWithMixedStatus(
+ function(item) {
+ _self.appendResultToOutput(kHTML4Data, item.test, item.hstatus, item.hcomment);
+ _self.appendResultToOutput(kXHTML1Data, item.test, item.xstatus, item.xcomment);
+ },
+ function() {
+ _self.endAppendingOutput();
+ }
+ );
+}
+
+TestSuite.prototype.exportResultsForTestsWithMismatchedResults = function()
+{
+ var exportTests = [];
+
+ var _self = this;
+ this.queryDatabaseForTestsWithMixedStatus(
+ function(item) {
+ var htmlLine= _self.createExportLine(kHTML4Data, item.test, item.hstatus, item.hcomment);
+ var xhtmlLine = _self.createExportLine(kXHTML1Data, item.test, item.xstatus, item.xcomment);
+ exportTests.push({
+ 'test' : item.test,
+ 'html4' : htmlLine,
+ 'xhtml1' : xhtmlLine });
+ },
+ function() {
+ _self.exportResultsCompletion(exportTests);
+ }
+ );
+}
+
+/* -------------------------------------------------------- */
+
+TestSuite.prototype.markTestCompleted = function(testID)
+{
+ var test = this.tests[testID];
+ if (!test) {
+ window.console.log('markTestCompleted to find test ' + testID);
+ return;
+ }
+
+ test.completed = true;
+}
+
+TestSuite.prototype.testCompletionStateChanged = function()
+{
+ // update the test list
+ this.updateTestList();
+}
+
+TestSuite.prototype.loadTestStatus = function()
+{
+ var _self = this;
+ this.queryDatabaseForCompletedTests(
+ function(item) {
+ _self.markTestCompleted(item.test);
+ },
+ function() {
+ _self.testCompletionStateChanged();
+ }
+ );
+}
+
+/* -------------------------------------------------------- */
+
+TestSuite.prototype.updateSummaryData = function()
+{
+ this.queryDatabaseForSummary(
+ function(results) {
+
+ var hTotal, xTotal;
+ var hDone, xDone;
+
+ for (var i = 0; i < results.length; ++i) {
+ var result = results[i];
+
+ switch (result.name) {
+ case 'h-total': hTotal = result.count; break;
+ case 'x-total': xTotal = result.count; break;
+ case 'h-tested': hDone = result.count; break;
+ case 'x-tested': xDone = result.count; break;
+ }
+
+ document.getElementById(result.name).innerText = result.count;
+ }
+
+ // We should get these all together.
+ if (hTotal) {
+ document.getElementById('h-percent').innerText = Math.round(100.0 * hDone / hTotal);
+ document.getElementById('x-percent').innerText = Math.round(100.0 * xDone / xTotal);
+ }
+ }
+ );
+}
+
+/* ------------------------------------------------------- */
+// Database stuff
+
+function errorHandler(transaction, error)
+{
+ alert('Database error: ' + error.message);
+ window.console.log('Database error: ' + error.message);
+}
+
+TestSuite.prototype.openDatabase = function()
+{
+ if (!'openDatabase' in window) {
+ alert('Your browser does not support client-side SQL databases, so results will not be stored.');
+ return;
+ }
+
+ var _self = this;
+ this.db = window.openDatabase('css21testsuite', '1.0', 'CSS 2.1 test suite results', 10 * 1024 * 1024, function() {
+ _self.databaseCreated();
+ }, errorHandler);
+
+ this.updateSummaryData();
+ this.loadTestStatus();
+}
+
+TestSuite.prototype.databaseCreated = function(db)
+{
+ this.populatingDatabase = true;
+
+ var _self = this;
+ this.db.transaction(function (tx) {
+ // hstatus: HTML4 result
+ // xstatus: XHTML1 result
+ tx.executeSql('CREATE TABLE tests (test PRIMARY KEY UNIQUE, ref, title, flags, links, assertion, hstatus, hcomment, xstatus, xcomment)', null,
+ function(tx, results) {
+ _self.populateDatabaseFromTestInfoData();
+ }, errorHandler);
+ });
+}
+
+TestSuite.prototype.storeTestResult = function(test, format, result, comment, useragent)
+{
+ if (!this.db)
+ return;
+
+ this.db.transaction(function (tx) {
+ if (format == 'html4')
+ tx.executeSql('UPDATE tests SET hstatus=?, hcomment=? WHERE test=?\n', [result, comment, test], null, errorHandler);
+ else if (format == 'xhtml1')
+ tx.executeSql('UPDATE tests SET xstatus=?, xcomment=? WHERE test=?\n', [result, comment, test], null, errorHandler);
+ });
+}
+
+TestSuite.prototype.populateDatabaseFromTestInfoData = function(testInfoURL)
+{
+ if (!this.testInfoLoaded) {
+ window.console.log('Tring to populate database before testinfo.data has been loaded');
+ return;
+ }
+
+ var _self = this;
+ this.db.transaction(function (tx) {
+ for (var testID in _self.tests) {
+ var currTest = _self.tests[testID];
+ tx.executeSql('INSERT INTO tests (test, ref, title, flags, links, assertion) VALUES (?, ?, ?, ?, ?, ?)', [currTest.id, currTest.reference, currTest.title, currTest.flags, currTest.links, currTest.assertion], null, errorHandler);
+ }
+
+ _self.populatingDatabase = false;
+ });
+
+}
+
+TestSuite.prototype.queryDatabaseForAllTests = function(sortKey, perRowHandler, completionHandler)
+{
+ if (this.populatingDatabase)
+ return;
+
+ var _self = this;
+ this.db.transaction(function (tx) {
+ if (_self.populatingDatabase)
+ return;
+ var query;
+ var args = [];
+ if (sortKey != '') {
+ query = 'SELECT * FROM tests ORDER BY ? ASC'; // ORDER BY doesn't seem to work
+ args.push(sortKey);
+ }
+ else
+ query = 'SELECT * FROM tests';
+
+ tx.executeSql(query, args, function(tx, results) {
+
+ var len = results.rows.length;
+ for (var i = 0; i < len; ++i)
+ perRowHandler(results.rows.item(i));
+
+ completionHandler();
+ }, errorHandler);
+ });
+}
+
+TestSuite.prototype.queryDatabaseForTestsWithStatus = function(status, perRowHandler, completionHandler)
+{
+ if (this.populatingDatabase)
+ return;
+
+ var _self = this;
+ this.db.transaction(function (tx) {
+ if (_self.populatingDatabase)
+ return;
+ tx.executeSql('SELECT * FROM tests WHERE hstatus=? OR xstatus=?', [status, status], function(tx, results) {
+
+ var len = results.rows.length;
+ for (var i = 0; i < len; ++i)
+ perRowHandler(results.rows.item(i));
+
+ completionHandler();
+ }, errorHandler);
+ });
+}
+
+TestSuite.prototype.queryDatabaseForTestsWithMixedStatus = function(perRowHandler, completionHandler)
+{
+ if (this.populatingDatabase)
+ return;
+
+ var _self = this;
+ this.db.transaction(function (tx) {
+ if (_self.populatingDatabase)
+ return;
+ tx.executeSql('SELECT * FROM tests WHERE hstatus IS NOT NULL AND xstatus IS NOT NULL AND hstatus <> xstatus', [], function(tx, results) {
+
+ var len = results.rows.length;
+ for (var i = 0; i < len; ++i)
+ perRowHandler(results.rows.item(i));
+
+ completionHandler();
+ }, errorHandler);
+ });
+}
+
+TestSuite.prototype.queryDatabaseForCompletedTests = function(perRowHandler, completionHandler)
+{
+ if (this.populatingDatabase)
+ return;
+
+ var _self = this;
+ this.db.transaction(function (tx) {
+
+ if (_self.populatingDatabase)
+ return;
+
+ tx.executeSql('SELECT * FROM tests WHERE hstatus IS NOT NULL OR xstatus IS NOT NULL', [], function(tx, results) {
+ var len = results.rows.length;
+ for (var i = 0; i < len; ++i)
+ perRowHandler(results.rows.item(i));
+
+ completionHandler();
+ }, errorHandler);
+ });
+}
+
+TestSuite.prototype.queryDatabaseForTestsNotRun = function(perRowHandler, completionHandler)
+{
+ if (this.populatingDatabase)
+ return;
+
+ var _self = this;
+ this.db.transaction(function (tx) {
+ if (_self.populatingDatabase)
+ return;
+
+ tx.executeSql('SELECT * FROM tests WHERE hstatus IS NULL OR xstatus IS NULL', [], function(tx, results) {
+
+ var len = results.rows.length;
+ for (var i = 0; i < len; ++i)
+ perRowHandler(results.rows.item(i));
+
+ completionHandler();
+ }, errorHandler);
+ });
+}
+
+/*
+
+ completionHandler gets called an array of results,
+ which may be some or all of:
+
+ data = [
+ { 'name' : ,
+ 'count' :
+ },
+ ]
+
+ where name is one of:
+
+ 'h-total'
+ 'h-tested'
+ 'h-passed'
+ 'h-failed'
+ 'h-skipped'
+
+ 'x-total'
+ 'x-tested'
+ 'x-passed'
+ 'x-failed'
+ 'x-skipped'
+
+ */
+
+
+TestSuite.prototype.countTestsWithColumnValue = function(tx, completionHandler, column, value, label)
+{
+ var allRowsCount = 'COUNT(*)';
+
+ tx.executeSql('SELECT COUNT(*) FROM tests WHERE ' + column + '=?', [value], function(tx, results) {
+ var data = [];
+ if (results.rows.length > 0)
+ data.push({ 'name' : label, 'count' : results.rows.item(0)[allRowsCount] })
+ completionHandler(data);
+ }, errorHandler);
+}
+
+TestSuite.prototype.queryDatabaseForSummary = function(completionHandler)
+{
+ if (!this.db || this.populatingDatabase)
+ return;
+
+ var _self = this;
+ this.db.transaction(function (tx) {
+
+ if (_self.populatingDatabase)
+ return;
+
+ var allRowsCount = 'COUNT(*)';
+ var html4RowsCount = 'COUNT(hstatus)';
+ var xhtml1RowsCount = 'COUNT(xstatus)';
+
+ tx.executeSql('SELECT COUNT(*), COUNT(hstatus), COUNT(xstatus) FROM tests', [], function(tx, results) {
+
+ var data = [];
+ if (results.rows.length > 0) {
+ var rowItem = results.rows.item(0);
+ data.push({ 'name' : 'h-total' , 'count' : rowItem[allRowsCount] })
+ data.push({ 'name' : 'x-total' , 'count' : rowItem[allRowsCount] })
+ data.push({ 'name' : 'h-tested', 'count' : rowItem[html4RowsCount] })
+ data.push({ 'name' : 'x-tested', 'count' : rowItem[xhtml1RowsCount] })
+ }
+ completionHandler(data);
+
+ }, errorHandler);
+
+ _self.countTestsWithColumnValue(tx, completionHandler, 'hstatus', 'pass', 'h-passed');
+ _self.countTestsWithColumnValue(tx, completionHandler, 'xstatus', 'pass', 'x-passed');
+
+ _self.countTestsWithColumnValue(tx, completionHandler, 'hstatus', 'fail', 'h-failed');
+ _self.countTestsWithColumnValue(tx, completionHandler, 'xstatus', 'fail', 'x-failed');
+
+ _self.countTestsWithColumnValue(tx, completionHandler, 'hstatus', 'skipped', 'h-skipped');
+ _self.countTestsWithColumnValue(tx, completionHandler, 'xstatus', 'skipped', 'x-skipped');
+
+ _self.countTestsWithColumnValue(tx, completionHandler, 'hstatus', 'invalid', 'h-invalid');
+ _self.countTestsWithColumnValue(tx, completionHandler, 'xstatus', 'invalid', 'x-invalid');
+ });
+}
+