From ef68436ac04da078ffdcacd7e1f785473a303d45 Mon Sep 17 00:00:00 2001 From: Giulio Cesare Solaroli Date: Sun, 02 Oct 2011 23:56:18 +0000 Subject: First version of the newly restructured repository --- (limited to 'frontend/gamma/tests/SimpleTest') diff --git a/frontend/gamma/tests/SimpleTest/SimpleTest.Async.js b/frontend/gamma/tests/SimpleTest/SimpleTest.Async.js new file mode 100644 index 0000000..040cdbf --- a/dev/null +++ b/frontend/gamma/tests/SimpleTest/SimpleTest.Async.js @@ -0,0 +1,169 @@ +/* + +Copyright 2008-2011 Clipperz Srl + +This file is part of Clipperz's Javascript Crypto Library. +Javascript Crypto Library provides web developers with an extensive +and efficient set of cryptographic functions. The library aims to +obtain maximum execution speed while preserving modularity and +reusability. +For further information about its features and functionalities please +refer to http://www.clipperz.com + +* Javascript Crypto Library is free software: you can redistribute + it and/or modify it under the terms of the GNU Affero General Public + License as published by the Free Software Foundation, either version + 3 of the License, or (at your option) any later version. + +* Javascript Crypto Library is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Affero General Public License for more details. + +* You should have received a copy of the GNU Affero General Public + License along with Javascript Crypto Library. If not, see + . + +*/ + +MochiKit.Base.update(Clipperz.Async.Deferred.prototype, { +/* + '_addTest': function(anExpectedValue, aDescription, isDeep, aResult) { + if (isDeep) { + SimpleTest.isDeeply(aResult, anExpectedValue, aDescription); + } else { + SimpleTest.is(aResult, anExpectedValue, aDescription); + } + + return aResult; + }, +*/ + 'addTest': function (anExpectedValue, aDescription, isDeep) { +// this.addMethod(this, '_addTest', anExpectedValue, aDescription, isDeep); +// this.addCallback(Clipperz.Async.test, anExpectedValue, aDescription, isDeep); + + if (isDeep) { +// SimpleTest.isDeeply(aResult, anExpectedValue, aDescription); + this.addCallback(Clipperz.Async.Test.isDeeply(anExpectedValue, aDescription)); + } else { +// SimpleTest.is(aResult, anExpectedValue, aDescription); + this.addCallback(Clipperz.Async.Test.is(anExpectedValue, aDescription)); + } + }, + + //------------------------------------------------------------------------- + + 'shouldSucceed': function (aDescription) { + this.addCallbackPass(SimpleTest.ok, true, aDescription); + this.addErrbackPass (SimpleTest.ok, false, aDescription); + this.addBoth(MochiKit.Async.succeed, null); + }, + + 'shouldFail': function (aDescription) { + this.addCallbackPass(SimpleTest.ok, false, aDescription); + this.addErrbackPass (SimpleTest.ok, true, aDescription); + this.addBoth(MochiKit.Async.succeed, null); + }, + + //------------------------------------------------------------------------- + +}); + + +Clipperz.Async.Test = {}; +MochiKit.Base.update(Clipperz.Async.Test, { + + 'is': function (anExpectedResult, aDescription) { + return MochiKit.Base.partial(function (anExpectedResult, aDescription, aResult) { + SimpleTest.is(aResult, anExpectedResult, aDescription); + + return aResult; + }, anExpectedResult, aDescription); + }, + + //------------------------------------------------------------------------- + + 'ok': function (aDescription) { + return MochiKit.Base.partial(function (aDescription, aResult) { + SimpleTest.ok(aResult, aDescription); + + return aResult; + }, aDescription); + }, + + //------------------------------------------------------------------------- + + 'fail': function(aDescription) { + return MochiKit.Base.partial(function (aDescription, aResult) { + SimpleTest.ok(!aResult, aDescription); + + return aResult; + }, aDescription); + }, + + //------------------------------------------------------------------------- + + 'isDeeply': function (anExpectedResult, aDescription) { + return MochiKit.Base.partial(function (anExpectedResult, aDescription, aResult) { + SimpleTest.isDeeply(aResult, anExpectedResult, aDescription); + + return aResult; + }, anExpectedResult, aDescription); + }, + + //------------------------------------------------------------------------- + __syntaxFix__: "syntax fix" +}); + + +SimpleTest.runDeferredTests = function (aName, aTestSet, aTestArguments) { + try { + var deferredTests; + var aTestName; + var title; + + deferredTests = new Clipperz.Async.Deferred(aName + " ", aTestArguments); + + title = aName; + + aTestName = window.location.href.match(/#.*/); + if (aTestName && (aTestName != '#')) { + aTestName = aTestName[0].slice(1); + if (aTestName in aTestSet) { + //Clipperz.log("single test execution, via fragment identifier", aTestName); + title += ' [' + aTestName + ']'; + deferredTests.addCallback(aTestSet[aTestName], aTestArguments); + deferredTests.addErrback(SimpleTest.ok, false, aTestName); + } else { + title = 'WRONG TEST NAME' + deferredTests.addBoth(is, aTestName, null, "Wrong test name selected to run"); + } + } else { + for (aTestName in aTestSet) { + deferredTests.addCallback(aTestSet[aTestName], aTestArguments); + deferredTests.addErrback(SimpleTest.ok, false, aTestName); + deferredTests.addBoth(MochiKit.Async.wait, 0.5); + } + deferredTests.addBoth(is, true, true, "FINISH: completed the full stack of tests"); + } + + MochiKit.DOM.currentDocument().title = title; + + deferredTests.addBoth(SimpleTest.finish); + deferredTests.callback(); + + SimpleTest.waitForExplicitFinish(); + } catch (err) { + var s = "test suite failure!\n"; + var o = {}; + var k = null; + for (k in err) { + // ensure unique keys?! + if (!o[k]) { + s += k + ": " + err[k] + "\n"; + o[k] = err[k]; + } + } + ok ( false, s ); + } +} diff --git a/frontend/gamma/tests/SimpleTest/SimpleTest.js b/frontend/gamma/tests/SimpleTest/SimpleTest.js new file mode 100644 index 0000000..418954f --- a/dev/null +++ b/frontend/gamma/tests/SimpleTest/SimpleTest.js @@ -0,0 +1,424 @@ +/** + * SimpleTest, a partial Test.Simple/Test.More API compatible test library. + * + * Why? + * + * Test.Simple doesn't work on IE < 6. + * TODO: + * * Support the Test.Simple API used by MochiKit, to be able to test MochiKit + * itself against IE 5.5 + * +**/ + +if (typeof(SimpleTest) == "undefined") { + var SimpleTest = {}; +} + +// Check to see if the TestRunner is present and has logging +if (typeof(parent) != "undefined" && parent.TestRunner) { + SimpleTest._logEnabled = parent.TestRunner.logEnabled; +} + +SimpleTest._tests = []; +SimpleTest._stopOnLoad = true; +SimpleTest._scopeCopy = {}; + +/** + * Saves a copy of the specified scope variables. + */ +SimpleTest.saveScope = function (scope) { + SimpleTest._scopeCopy = {}; + for (var k in scope) { + SimpleTest._scopeCopy[k] = scope[k]; + } +} + +/** + * Verifies the specified scope against the stored copy and reports + * any differences as test failures. + */ +SimpleTest.verifyScope = function (scope) { + var filter = ['test', '_firebug','_FirebugConsole','XMLHttpRequest','Audio', + 'XSLTProcessor','Option','Image','scrollMaxX','scrollMaxY', + 'clipboardData']; + for (var k in scope) { + if (MochiKit.Base.findValue(filter, k) < 0) { + var v = scope[k]; + var old = SimpleTest._scopeCopy[k]; + if (v !== old && typeof(old) === "undefined") { + SimpleTest.ok(false, "scope modified, variable " + k + " was added"); + } else if (v !== old) { + SimpleTest.ok(false, "scope modified, variable " + k + " changed from: " + old + ", to: " + v); + } + } + } + for (var k in SimpleTest._scopeCopy) { + if (!(k in scope)) { + SimpleTest.ok(false, "scope modified, variable " + k + " has been removed"); + } + } +} + +/** + * Something like assert. + */ +SimpleTest.ok = function (condition, name, diag) { + var test = {'result': !!condition, 'name': name, 'diag': diag || ""}; + if (SimpleTest._logEnabled) { + var msg = test.result ? "PASS" : "FAIL"; + msg += " | " + test.name; + if (test.result) { + parent.TestRunner.logger.log(msg); + } else { + msg += " | " + test.diag; + parent.TestRunner.logger.error(msg); + } + } + SimpleTest._tests.push(test); +}; + +/** + * Roughly equivalent to ok(a==b, name) + */ +SimpleTest.is = function (a, b, name) { + var repr = MochiKit.Base.repr; + SimpleTest.ok(a == b, name, "got " + repr(a) + ", expected " + repr(b)); +}; + +/** + * Roughly equivalent to ok(compare(a,b)==0, name) + */ +SimpleTest.eq = function (a, b, name) { + var base = MochiKit.Base; + var repr = base.repr; + try { + SimpleTest.ok(base.compare(a, b) == 0, name, "got " + repr(a) + ", expected " + repr(b)); + } catch (e) { + SimpleTest.ok(false, name, "exception in compare: " + repr(e)); + } +}; + + +/** + * Makes a test report, returns it as a DIV element. +**/ +SimpleTest.report = function () { + var DIV = MochiKit.DOM.DIV; + var passed = 0; + var failed = 0; + var results = MochiKit.Base.map( + function (test) { + var cls, msg; + if (test.result) { + passed++; + cls = "test_ok"; + msg = "ok - " + test.name; + } else { + failed++; + cls = "test_not_ok"; + msg = "not ok - " + test.name; + if (test.diag != null && test.diag != "") { + msg += ": " + test.diag; + } + } + return DIV({"class": cls}, msg); + }, + SimpleTest._tests + ); + var summary_class = ((failed == 0) ? 'all_pass' : 'some_fail'); + return DIV({'class': 'tests_report'}, + DIV({'class': 'tests_summary ' + summary_class}, + DIV({'class': 'tests_passed'}, "Passed: " + passed), + DIV({'class': 'tests_failed'}, "Failed: " + failed)), + results + ); +}; + +/** + * Toggle element visibility +**/ +SimpleTest.toggle = function(el) { + if (MochiKit.Style.getStyle(el, 'display') == 'block') { + el.style.display = 'none'; + } else { + el.style.display = 'block'; + } +}; + +/** + * Toggle visibility for divs with a specific class. +**/ +SimpleTest.toggleByClass = function (cls) { + var elems = MochiKit.DOM.getElementsByTagAndClassName('div', cls); + MochiKit.Base.map(SimpleTest.toggle, elems); + return false; +}; + +/** + * Shows the report in the browser +**/ + +SimpleTest.showReport = function() { + var base = MochiKit.Base; + var dom = MochiKit.DOM; + var togglePassed = dom.A({'href': '#'}, "Toggle passed tests"); + var toggleFailed = dom.A({'href': '#'}, "Toggle failed tests"); + togglePassed.onclick = base.partial(SimpleTest.toggleByClass, 'test_ok'); + toggleFailed.onclick = base.partial(SimpleTest.toggleByClass, 'test_not_ok'); + var body = document.getElementsByTagName("body")[0]; + var firstChild = body.childNodes[0]; + var addNode; + if (firstChild) { + addNode = function (el) { + body.insertBefore(el, firstChild); + }; + } else { + addNode = function (el) { + body.appendChild(el) + }; + } + addNode(togglePassed); + addNode(dom.SPAN(null, " ")); + addNode(toggleFailed); + addNode(SimpleTest.report()); +}; + +/** + * Tells SimpleTest to don't finish the test when the document is loaded, + * useful for asynchronous tests. + * + * When SimpleTest.waitForExplicitFinish is called, + * explicit SimpleTest.finish() is required. +**/ +SimpleTest.waitForExplicitFinish = function () { + SimpleTest._stopOnLoad = false; +}; + +/** + * Talks to the TestRunner if being ran on a iframe and the parent has a + * TestRunner object. +**/ +SimpleTest.talkToRunner = function () { + if (typeof(parent) != "undefined" && parent.TestRunner) { + parent.TestRunner.testFinished(document); + } +}; + +/** + * Finishes the tests. This is automatically called, except when + * SimpleTest.waitForExplicitFinish() has been invoked. +**/ +SimpleTest.finish = function () { + SimpleTest.showReport(); + SimpleTest.talkToRunner(); +}; + + +MochiKit.DOM.addLoadEvent(function() { + if (SimpleTest._stopOnLoad) { + SimpleTest.finish(); + } +}); + +// --------------- Test.Builder/Test.More isDeeply() ----------------- + + +SimpleTest.DNE = {dne: 'Does not exist'}; +SimpleTest.LF = "\r\n"; +SimpleTest._isRef = function (object) { + var type = typeof(object); + return type == 'object' || type == 'function'; +}; + + +SimpleTest._deepCheck = function (e1, e2, stack, seen) { + var ok = false; + // Either they're both references or both not. + var sameRef = !(!SimpleTest._isRef(e1) ^ !SimpleTest._isRef(e2)); + if (e1 == null && e2 == null) { + ok = true; + } else if (e1 != null ^ e2 != null) { + ok = false; + } else if (e1 == SimpleTest.DNE ^ e2 == SimpleTest.DNE) { + ok = false; + } else if (sameRef && e1 == e2) { + // Handles primitives and any variables that reference the same + // object, including functions. + ok = true; + } else if (SimpleTest.isa(e1, 'Array') && SimpleTest.isa(e2, 'Array')) { + ok = SimpleTest._eqArray(e1, e2, stack, seen); + } else if (typeof e1 == "object" && typeof e2 == "object") { + ok = SimpleTest._eqAssoc(e1, e2, stack, seen); + } else { + // If we get here, they're not the same (function references must + // always simply rererence the same function). + stack.push({ vals: [e1, e2] }); + ok = false; + } + return ok; +}; + +SimpleTest._eqArray = function (a1, a2, stack, seen) { + // Return if they're the same object. + if (a1 == a2) return true; + + // JavaScript objects have no unique identifiers, so we have to store + // references to them all in an array, and then compare the references + // directly. It's slow, but probably won't be much of an issue in + // practice. Start by making a local copy of the array to as to avoid + // confusing a reference seen more than once (such as [a, a]) for a + // circular reference. + for (var j = 0; j < seen.length; j++) { + if (seen[j][0] == a1) { + return seen[j][1] == a2; + } + } + + // If we get here, we haven't seen a1 before, so store it with reference + // to a2. + seen.push([ a1, a2 ]); + + var ok = true; + // Only examines enumerable attributes. Only works for numeric arrays! + // Associative arrays return 0. So call _eqAssoc() for them, instead. + var max = a1.length > a2.length ? a1.length : a2.length; + if (max == 0) return SimpleTest._eqAssoc(a1, a2, stack, seen); + for (var i = 0; i < max; i++) { + var e1 = i > a1.length - 1 ? SimpleTest.DNE : a1[i]; + var e2 = i > a2.length - 1 ? SimpleTest.DNE : a2[i]; + stack.push({ type: 'Array', idx: i, vals: [e1, e2] }); + if (ok = SimpleTest._deepCheck(e1, e2, stack, seen)) { + stack.pop(); + } else { + break; + } + } + return ok; +}; + +SimpleTest._eqAssoc = function (o1, o2, stack, seen) { + // Return if they're the same object. + if (o1 == o2) return true; + + // JavaScript objects have no unique identifiers, so we have to store + // references to them all in an array, and then compare the references + // directly. It's slow, but probably won't be much of an issue in + // practice. Start by making a local copy of the array to as to avoid + // confusing a reference seen more than once (such as [a, a]) for a + // circular reference. + seen = seen.slice(0); + for (var j = 0; j < seen.length; j++) { + if (seen[j][0] == o1) { + return seen[j][1] == o2; + } + } + + // If we get here, we haven't seen o1 before, so store it with reference + // to o2. + seen.push([ o1, o2 ]); + + // They should be of the same class. + + var ok = true; + // Only examines enumerable attributes. + var o1Size = 0; for (var i in o1) o1Size++; + var o2Size = 0; for (var i in o2) o2Size++; + var bigger = o1Size > o2Size ? o1 : o2; + for (var i in bigger) { + var e1 = o1[i] == undefined ? SimpleTest.DNE : o1[i]; + var e2 = o2[i] == undefined ? SimpleTest.DNE : o2[i]; + stack.push({ type: 'Object', idx: i, vals: [e1, e2] }); + if (ok = SimpleTest._deepCheck(e1, e2, stack, seen)) { + stack.pop(); + } else { + break; + } + } + return ok; +}; + +SimpleTest._formatStack = function (stack) { + var variable = '$Foo'; + for (var i = 0; i < stack.length; i++) { + var entry = stack[i]; + var type = entry['type']; + var idx = entry['idx']; + if (idx != null) { + if (/^\d+$/.test(idx)) { + // Numeric array index. + variable += '[' + idx + ']'; + } else { + // Associative array index. + idx = idx.replace("'", "\\'"); + variable += "['" + idx + "']"; + } + } + } + + var vals = stack[stack.length-1]['vals'].slice(0, 2); + var vars = [ + variable.replace('$Foo', 'got'), + variable.replace('$Foo', 'expected') + ]; + + var out = "Structures begin differing at:" + SimpleTest.LF; + for (var i = 0; i < vals.length; i++) { + var val = vals[i]; + if (val == null) { + val = 'undefined'; + } else { + val == SimpleTest.DNE ? "Does not exist" : "'" + val + "'"; + } + } + + out += vars[0] + ' = ' + vals[0] + SimpleTest.LF; + out += vars[1] + ' = ' + vals[1] + SimpleTest.LF; + + return ' ' + out; +}; + + +SimpleTest.isDeeply = function (it, as, name) { + var ok; + // ^ is the XOR operator. + if (SimpleTest._isRef(it) ^ SimpleTest._isRef(as)) { + // One's a reference, one isn't. + ok = false; + } else if (!SimpleTest._isRef(it) && !SimpleTest._isRef(as)) { + // Neither is an object. + ok = SimpleTest.is(it, as, name); + } else { + // We have two objects. Do a deep comparison. + var stack = [], seen = []; + if ( SimpleTest._deepCheck(it, as, stack, seen)) { + ok = SimpleTest.ok(true, name); + } else { + ok = SimpleTest.ok(false, name, SimpleTest._formatStack(stack)); + } + } + return ok; +}; + +SimpleTest.typeOf = function (object) { + var c = Object.prototype.toString.apply(object); + var name = c.substring(8, c.length - 1); + if (name != 'Object') return name; + // It may be a non-core class. Try to extract the class name from + // the constructor function. This may not work in all implementations. + if (/function ([^(\s]+)/.test(Function.toString.call(object.constructor))) { + return RegExp.$1; + } + // No idea. :-( + return name; +}; + +SimpleTest.isa = function (object, clas) { + return SimpleTest.typeOf(object) == clas; +}; + + + +// Global symbols: +var ok = SimpleTest.ok; +var is = SimpleTest.is; +var isDeeply = SimpleTest.isDeeply; diff --git a/frontend/gamma/tests/SimpleTest/TestRunner.js b/frontend/gamma/tests/SimpleTest/TestRunner.js new file mode 100644 index 0000000..81a36cf --- a/dev/null +++ b/frontend/gamma/tests/SimpleTest/TestRunner.js @@ -0,0 +1,233 @@ +/** + * TestRunner: A test runner for SimpleTest + * TODO: + * + * * Avoid moving iframes: That causes reloads on mozilla and opera. + * + * +**/ +var TestRunner = {}; +TestRunner.logEnabled = false; +TestRunner._iframes = {}; +TestRunner._iframeDocuments = {}; +TestRunner._iframeRows = {}; +TestRunner._currentTest = 0; +TestRunner._urls = []; +TestRunner._testsDiv = DIV(); +TestRunner._progressDiv = DIV(); +TestRunner._summaryDiv = DIV(null, + H1(null, "Tests Summary"), + TABLE(null, + THEAD(null, + TR(null, + TH(null, "Test"), + TH(null, "Passed"), + TH(null, "Failed") + ) + ), + TFOOT(/*null, TR(null, TH(null, "-"), TH(null, "--"), TH(null, "---"))*/), + TBODY() + ) +); + +/** + * This function is called after generating the summary. +**/ +TestRunner.onComplete = null; + +/** + * If logEnabled is true, this is the logger that will be used. +**/ +TestRunner.logger = MochiKit.Logging.logger; + +/** + * Toggle element visibility +**/ +TestRunner._toggle = function(el) { + if (el.className == "noshow") { + el.className = ""; + el.style.cssText = ""; + } else { + el.className = "noshow"; + el.style.cssText = "width:0px; height:0px; border:0px;"; + } +}; + + +/** + * Creates the iframe that contains a test +**/ +TestRunner._makeIframe = function (url) { + var iframe = document.createElement('iframe'); + iframe.src = url; + iframe.name = url; + iframe.width = "500"; + var tbody = TestRunner._summaryDiv.getElementsByTagName("tbody")[0]; + var tr = TR(null, TD({'colspan': '3'}, iframe)); + iframe._row = tr; + tbody.appendChild(tr); + return iframe; +}; + +/** + * TestRunner entry point. + * + * The arguments are the URLs of the test to be ran. + * +**/ +TestRunner.runTests = function (/*url...*/) { + if (TestRunner.logEnabled) + TestRunner.logger.log("SimpleTest START"); + + var body = document.getElementsByTagName("body")[0]; + appendChildNodes(body, + TestRunner._testsDiv, + TestRunner._progressDiv, + TestRunner._summaryDiv + ); + for (var i = 0; i < arguments.length; i++) { + TestRunner._urls.push(arguments[i]); + } + + TestRunner.runNextTest(); +}; + +/** + * Run the next test. If no test remains, calls makeSummary +**/ +TestRunner.runNextTest = function() { + if (TestRunner._currentTest < TestRunner._urls.length) { + var url = TestRunner._urls[TestRunner._currentTest]; + var progress = SPAN(null, + "Running ", A({href:url}, url), "..." + ); + + if (TestRunner.logEnabled) + TestRunner.logger.log(scrapeText(progress)); + + TestRunner._progressDiv.appendChild(progress); + TestRunner._iframes[url] = TestRunner._makeIframe(url); + } else { + TestRunner.makeSummary(); + if (TestRunner.onComplete) { + TestRunner.onComplete(); + } + } +}; + +/** + * This stub is called by SimpleTest when a test is finished. +**/ +TestRunner.testFinished = function (doc) { + appendChildNodes(TestRunner._progressDiv, SPAN(null, "Done"), BR()); + var finishedURL = TestRunner._urls[TestRunner._currentTest]; + + if (TestRunner.logEnabled) + TestRunner.logger.debug("SimpleTest finished " + finishedURL); + + TestRunner._iframeDocuments[finishedURL] = doc; + // TestRunner._iframes[finishedURL].style.display = "none"; + if (finishedURL != null) { + TestRunner._toggle(TestRunner._iframes[finishedURL]); + TestRunner._currentTest++; + TestRunner.runNextTest(); + } +}; + +/** + * Display the summary in the browser +**/ +/* +TestRunner.makeSummary = function() { + if (TestRunner.logEnabled) + TestRunner.logger.log("SimpleTest FINISHED"); + var rows = []; + for (var url in TestRunner._iframeDocuments) { + var doc = TestRunner._iframeDocuments[url]; + var nOK = withDocument(doc, + partial(getElementsByTagAndClassName, 'div', 'test_ok') + ).length; + var nNotOK = withDocument(doc, + partial(getElementsByTagAndClassName, 'div', 'test_not_ok') + ).length; + var toggle = partial(TestRunner._toggle, TestRunner._iframes[url]); + var jsurl = "TestRunner._toggle(TestRunner._iframes['" + url + "'])"; + var row = TR( + {'style': {'backgroundColor': nNotOK > 0 ? "#f00":"#0f0"}}, + TD(null, url), + TD(null, nOK), + TD(null, nNotOK) + ); + row.onclick = toggle; + var tbody = TestRunner._summaryDiv.getElementsByTagName("tbody")[0]; + tbody.insertBefore(row, TestRunner._iframes[url]._row) + } +}; +*/ + +TestRunner.makeSummary = function() { + var base = MochiKit.Base; + var dom = MochiKit.DOM; + var iter = MochiKit.Iter; + var total_Ok, total_not_Ok; + + total_Ok = 0; + total_not_Ok = 0; + + if (TestRunner.logEnabled) + TestRunner.logger.log("SimpleTest FINISHED"); + var rows = []; + for (var url in TestRunner._iframeDocuments) { + var doc = TestRunner._iframeDocuments[url]; + var nOK = withDocument(doc, + partial(getElementsByTagAndClassName, 'div', 'test_ok') + ).length; + var nNotOK = withDocument(doc, + partial(getElementsByTagAndClassName, 'div', 'test_not_ok') + ).length; + var toggle = partial(TestRunner._toggle, TestRunner._iframes[url]); + var jsurl = "TestRunner._toggle(TestRunner._iframes['" + url + "'])"; + var row = TR( + {'style': {'backgroundColor': nNotOK > 0 ? "#f00":"#0f0"}}, + TD(null, url), + TD(null, nOK), + TD(null, nNotOK) + ); + row.onclick = toggle; + var tbody = TestRunner._summaryDiv.getElementsByTagName("tbody")[0]; + tbody.insertBefore(row, TestRunner._iframes[url]._row); + + total_Ok += nOK; + total_not_Ok += nNotOK; + } + + { + var tfoot = TestRunner._summaryDiv.getElementsByTagName("tfoot")[0]; + tfoot.appendChild(TR(null, + TH(null, ""), + TH({'style':"color:green;"}, total_Ok), + TH({'style':"color:" + ((total_not_Ok == 0) ? 'black' : 'red') + ";"}, total_not_Ok) + )); + } + + var testRunnerResults; + var i, c; + + testRunnerResults = dom.DIV({'style': 'display:none; visibility:hidden;'}, null); + + c = total_Ok; + for (i=0; i