Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This file expects utils.js to be included in its scope
/* import-globals-from ./util.js */
var MOCHITEST = false;
function Test(name, test) {
this.name = name;
this.startTime = null;
this.endTime = null;
this.result = null;
this.row = null;
this.run = function () {
// Note the start time
this.startTime = new Date();
// Run the test
try {
test.call(this);
} catch (e) {
console.log(e);
console.log(e.stack);
this.complete(false);
}
};
this.memcmp_complete = function (x, y) {
var passfail = util.memcmp(x, y);
if (!passfail) {
console.log("expected: " + util.abv2hex(x));
console.log(" got: " + util.abv2hex(y));
}
this.complete(passfail);
};
this.complete = function (result) {
if (MOCHITEST) {
ok(result, this.name);
}
// Note the end time
this.endTime = new Date();
// Set result
this.result = result;
// Re-draw the row
this.draw();
this.next();
};
this.next = function () {
if (this.oncomplete) {
this.oncomplete();
}
};
this.setRow = function (id) {
this.row = document.getElementById(id).getElementsByTagName("td");
};
this.draw = function () {
if (!this.row) {
return;
}
// Print the name of the test
if (this.name) {
this.row[0].textContent = this.name;
var that = this;
this.row[0].onclick = function () {
that.run();
};
} else {
this.row[0] = "";
}
// Print the result of the test
if (this.result === true) {
this.row[1].className = "pass";
this.row[1].innerHTML = "PASS";
} else if (this.result === false) {
this.row[1].className = "fail";
this.row[1].innerHTML = "FAIL";
} else {
// this.row[1].innerHTML = "";
this.row[1].textContent = this.result;
}
// Print the elapsed time, if known
if (this.startTime && this.endTime) {
this.row[2].textContent = this.endTime - this.startTime + " ms";
} else {
this.row[2].innerHTML = "";
}
};
}
function WorkerTest(worker, name, test) {
this.name = `${name} (Worker)`;
this.startTime = null;
this.endTime = null;
this.result = null;
this.row = null;
this.run = function () {
// Note the start time
this.startTime = new Date();
// Send the test code to the worker.
worker.postMessage(test.toString());
// We expect only boolean responses from the worker script.
worker.onmessage = e => this.complete(e.data);
worker.onerror = () => this.complete(false);
};
var base = new Test(name, test);
// Inherit what we need from the |Test| class. We can't simply use its
// prototype as Test is just a function that can be used like a constructor.
for (var method of ["draw", "setRow", "next", "complete"]) {
this[method] = base[method].bind(this);
}
}
var TestArray = {
tests: [],
table: null,
passSpan: null,
failSpan: null,
pendingSpan: null,
pass: 0,
fail: 0,
pending: 0,
currTest: 0,
worker: new Worker("test-worker.js"),
addTest(name, testFn) {
// Give it a reference to the array
var test = new Test(name, testFn);
test.ta = this;
// Add test to tests
this.tests.push(test);
// Run the test in a worker too.
this.tests.push(new WorkerTest(this.worker, name, testFn));
},
updateSummary() {
this.pass = this.fail = this.pending = 0;
for (var i = 0; i < this.tests.length; ++i) {
if (this.tests[i].result === true) {
this.pass++;
}
if (this.tests[i].result === false) {
this.fail++;
}
if (this.tests[i].result == null) {
this.pending++;
}
}
this.passSpan.textContent = this.pass;
this.failSpan.textContent = this.fail;
this.pendingSpan.textContent = this.pending;
},
load() {
// Grab reference to table and summary numbers
this.table = document.getElementById("results");
this.passSpan = document.getElementById("passN");
this.failSpan = document.getElementById("failN");
this.pendingSpan = document.getElementById("pendingN");
// Populate everything initially
this.updateSummary();
for (var i = 0; i < this.tests.length; ++i) {
var tr = document.createElement("tr");
tr.id = "test" + i;
tr.appendChild(document.createElement("td"));
tr.appendChild(document.createElement("td"));
tr.appendChild(document.createElement("td"));
this.table.appendChild(tr);
this.tests[i].setRow(tr.id);
this.tests[i].draw();
}
},
run() {
this.currTest = 0;
this.runNextTest();
},
runNextTest() {
this.updateSummary();
var i = this.currTest++;
if (i >= this.tests.length) {
if (MOCHITEST) {
SimpleTest.finish();
}
return;
}
var self = this;
this.tests[i].oncomplete = function () {
self.runNextTest();
};
this.tests[i].run();
},
};
if (window.addEventListener) {
window.addEventListener("load", function () {
TestArray.load();
});
} else {
window.attachEvent("onload", function () {
TestArray.load();
});
}
function start() {
TestArray.run();
}
MOCHITEST = "SimpleTest" in window;
if (MOCHITEST) {
SimpleTest.waitForExplicitFinish();
SimpleTest.requestLongerTimeout(2);
window.addEventListener("load", function () {
SimpleTest.waitForFocus(start);
});
}
function error(test) {
return function (x) {
console.log("ERROR :: " + x);
test.complete(false);
throw x;
};
}
function complete(test, valid) {
return function (x) {
console.log("COMPLETE");
console.log(x);
if (valid) {
test.complete(valid(x));
} else {
test.complete(true);
}
};
}
function memcmp_complete(test, value) {
return function (x) {
console.log("COMPLETE");
console.log(x);
test.memcmp_complete(value, x);
};
}