Bring translator tester up to date with translator tester on the website, and add convenience methods to perform translator tests without loading testTranslators.html.

This commit is contained in:
Simon Kornblith 2012-02-27 18:50:24 -05:00
parent d61fdeda4c
commit 3aaebb5802
3 changed files with 206 additions and 36 deletions

View File

@ -8,6 +8,7 @@ table {
border-width: 0 0 1px 1px;
border-style: solid;
border-collapse: collapse;
width: 100%;
}
td, th {
@ -18,21 +19,19 @@ td, th {
}
.th-translator {
width: 250px;
max-width: 250px;
}
.th-status {
width: 100px;
max-width: 100px;
}
.th-pending, .th-supported, .th-succeeded, .th-failed, .th-mismatch {
width: 75px;
max-width: 75px;
}
.th-issues {
width: 250px;
max-width: 500px;
}
.status-succeeded, .supported-yes {

View File

@ -29,6 +29,7 @@ const TABLE_COLUMNS = ["Translator", "Supported", "Status", "Pending", "Succeede
var translatorTables = {},
translatorTestViews = {},
translatorTestViewsToRun = {},
translatorTestStats = {},
translatorBox,
outputBox,
allOutputView,
@ -187,7 +188,17 @@ TranslatorTestView.prototype.setLabel = function(label) {
var issue = issues[i];
var div = document.createElement("div"),
a = document.createElement("a");
a.textContent = issue.title+" (#"+issue.number+")";
var date = issue.updated_at;
date = new Date(Date.UTC(date.substr(0, 4), date.substr(5, 2)-1, date.substr(8, 2),
date.substr(11, 2), date.substr(14, 2), date.substr(17, 2)));
if("toLocaleFormat" in date) {
date = date.toLocaleFormat("%x");
} else {
date = date.getFullYear()+"-"+date.getMonth()+"-"+date.getDate();
}
a.textContent = issue.title+" (#"+issue.number+"; "+date+")";
a.setAttribute("href", issue.html_url);
a.setAttribute("target", "_blank");
div.appendChild(a);
@ -200,18 +211,14 @@ TranslatorTestView.prototype.setLabel = function(label) {
* Initializes TranslatorTestView given a translator and its type
*/
TranslatorTestView.prototype.initWithTranslatorAndType = function(translator, type) {
this._translatorID = translator.translatorID;
this.setLabel(translator.label);
this.isSupported = translator.runMode === Zotero.Translator.RUN_MODE_IN_BROWSER;
this._supported.appendChild(document.createTextNode(this.isSupported ? "Yes" : "No"));
this._supported.className = this.isSupported ? "supported-yes" : "supported-no";
this._translatorTester = new Zotero_TranslatorTester(translator, type, this._debug);
this.canRun = !!this._translatorTester.tests.length;
this.updateStatus(this._translatorTester);
this._type = type;
translatorTestViews[type].push(this);
translatorTables[this._type].appendChild(this._row);
}
@ -222,14 +229,11 @@ TranslatorTestView.prototype.unserialize = function(serializedData) {
this._outputView.addOutput(serializedData.output);
this.setLabel(serializedData.label);
this.isSupported = serializedData.isSupported;
this._supported.appendChild(document.createTextNode(this.isSupported ? "Yes" : "No"));
this._supported.className = this.isSupported ? "supported-yes" : "supported-no";
this._type = serializedData.type;
translatorTestViews[serializedData.type].push(this);
this.canRun = false;
this.updateStatus(serializedData);
this._type = serializedData.type;
translatorTables[this._type].appendChild(this._row);
}
@ -237,17 +241,7 @@ TranslatorTestView.prototype.unserialize = function(serializedData) {
* Initializes TranslatorTestView given a JSON-ified translatorTester
*/
TranslatorTestView.prototype.serialize = function(serializedData) {
return {
"translatorID":this._translatorID,
"type":this._type,
"output":this._outputView.getOutput(),
"label":this._label.textContent,
"isSupported":this.isSupported,
"pending":this._translatorTester.pending,
"failed":this._translatorTester.failed,
"succeeded":this._translatorTester.succeeded,
"unknown":this._translatorTester.unknown
};
return this._translatorTester.serialize();
}
/**
@ -258,6 +252,9 @@ TranslatorTestView.prototype.updateStatus = function(obj, status) {
this._status.removeChild(this._status.firstChild);
}
this._supported.textContent = obj.isSupported ? "Yes" : "No";
this._supported.className = obj.isSupported ? "supported-yes" : "supported-no";
var pending = typeof obj.pending === "object" ? obj.pending.length : obj.pending;
var succeeded = typeof obj.succeeded === "object" ? obj.succeeded.length : obj.succeeded;
var failed = typeof obj.failed === "object" ? obj.failed.length : obj.failed;
@ -307,6 +304,8 @@ TranslatorTestView.prototype.updateStatus = function(obj, status) {
this._succeeded.textContent = succeeded;
this._failed.textContent = failed;
this._unknown.textContent = unknown;
if(this._type) translatorTestStats[this._type].update();
}
/**
@ -331,6 +330,44 @@ TranslatorTestView.prototype.runTests = function(doneCallback) {
this._translatorTester.runTests(newCallback);
}
/**
* Gets overall stats for translators
*/
var TranslatorTestStats = function(translatorType) {
this.translatorType = translatorType
this.node = document.createElement("p");
};
TranslatorTestStats.prototype.update = function() {
var types = {
"Success":0,
"Data Mismatch":0,
"Partial Failure":0,
"Failure":0,
"Untested":0,
"Running":0,
"Pending":0,
"Not Run":0
};
var testViews = translatorTestViews[this.translatorType];
for(var i in testViews) {
var status = testViews[i]._status ? testViews[i]._status.textContent : "Not Run";
if(status in types) {
types[status] += 1;
}
}
var typeInfo = [];
for(var i in types) {
if(types[i]) {
typeInfo.push(i+": "+types[i]);
}
}
this.node.textContent = typeInfo.join(" | ");
};
/**
* Called when loaded
*/
@ -384,6 +421,8 @@ function init() {
var displayType = TRANSLATOR_TYPES[i];
var translatorType = displayType.toLowerCase();
translatorTestViews[translatorType] = [];
// create header
var h1 = document.createElement("h1");
h1.appendChild(document.createTextNode(displayType+" Translators "));
@ -409,6 +448,9 @@ function init() {
var translatorTable = document.createElement("table");
translatorTables[translatorType] = translatorTable;
translatorTestStats[translatorType] = new TranslatorTestStats(translatorType);
translatorBox.appendChild(translatorTestStats[translatorType].node);
// add headings to table
var headings = document.createElement("tr");
for(var j in TABLE_COLUMNS) {
@ -500,7 +542,6 @@ function jsonNotFound(str) {
* Called after translators are returned from main script
*/
function haveTranslators(translators, type) {
translatorTestViews[type] = [];
translatorTestViewsToRun[type] = [];
translators = translators.sort(function(a, b) {
@ -510,12 +551,12 @@ function haveTranslators(translators, type) {
for(var i in translators) {
var translatorTestView = new TranslatorTestView();
translatorTestView.initWithTranslatorAndType(translators[i], type);
translatorTestViews[type].push(translatorTestView);
if(translatorTestView.canRun) {
translatorTestViewsToRun[type].push(translatorTestView);
}
}
translatorTestStats[type].update();
var ev = document.createEvent('HTMLEvents');
ev.initEvent('ZoteroHaveTranslators-'+type, true, true);
document.dispatchEvent(ev);
@ -551,7 +592,7 @@ function initTests(type, callback, runCallbackIfComplete) {
* Serializes translator tests to JSON
*/
function serializeToJSON() {
var serializedData = {"browser":Zotero.browser, "results":[]};
var serializedData = {"browser":Zotero.browser, "version":Zotero.version, "results":[]};
for(var i in translatorTestViews) {
var n = translatorTestViews[i].length;
for(var j=0; j<n; j++) {

View File

@ -25,8 +25,110 @@
// Timeout for test to complete
const TEST_RUN_TIMEOUT = 600000;
var EXPORTED_SYMBOLS = ["Zotero_TranslatorTesters"];
var Zotero_TranslatorTester_IGNORE_FIELDS = ["complete", "accessDate", "checkFields"];
try {
Zotero;
} catch(e) {
var Zotero;
}
Zotero_TranslatorTesters = new function() {
const TEST_TYPES = ["web", "import", "export", "search"];
/**
* Runs all tests
*/
this.runAllTests = function(numConcurrentTests, skipTranslators, doneCallback) {
if(!Zotero) {
Zotero = Components.classes["@zotero.org/Zotero;1"]
.getService(Components.interfaces.nsISupports).wrappedJSObject;
}
var testers = [];
var waitingForTranslators = TEST_TYPES.length;
for(var i=0; i<TEST_TYPES.length; i++) {
Zotero.Translators.getAllForType(TEST_TYPES[i], new function() {
var type = TEST_TYPES[i];
return function(translators) {
for(var i=0; i<translators.length; i++) {
if(skipTranslators && !skipTranslators[translators[i].translatorID]) {
testers.push(new Zotero_TranslatorTester(translators[i], type));
}
};
if(!(--waitingForTranslators)) {
runTesters(testers, numConcurrentTests, doneCallback);
}
};
});
};
};
/**
* Runs a specific set of tests
*/
function runTesters(testers, numConcurrentTests, doneCallback) {
var testersRunning = 0;
var results = [];
var strcmp;
try {
var localeService = Components.classes["@mozilla.org/intl/nslocaleservice;1"]
.getService(Components.interfaces.nsILocaleService);
var collationFactory = Components.classes["@mozilla.org/intl/collation-factory;1"]
.getService(Components.interfaces.nsICollationFactory);
var collation = collationFactory.CreateCollation(localeService.getApplicationLocale());
strcmp = function(a, b) {
return collation.compareString(1, a, b);
};
} catch (e) {
strcmp = function (a, b) {
return a.localeCompare(b);
};
}
var testerDoneCallback = function(tester) {
if(tester.pending.length) return;
Zotero.debug("Done testing "+tester.translator.label);
// Done translating, so serialize test results
testersRunning--;
results.push(tester.serialize());
if(testers.length) {
// Run next tester if one is available
runNextTester();
} else if(testersRunning === 0) {
// Testing is done, so sort results
results.sort(function(a, b) {
if(a.type !== b.type) {
return TEST_TYPES.indexOf(a.type) - TEST_TYPES.indexOf(b.type);
}
return strcmp(a.label, b.label);
});
// Call done callback
doneCallback({
"browser":Zotero.browser,
"version":Zotero.version,
"results":results
});
}
};
var runNextTester = function() {
testersRunning++;
Zotero.debug("Testing "+testers[0].translator.label);
testers.shift().runTests(testerDoneCallback);
};
for(var i=0; i<numConcurrentTests; i++) {
runNextTester();
};
}
}
/**
* A tool to run unit tests for a given translator
@ -39,13 +141,14 @@ var Zotero_TranslatorTester_IGNORE_FIELDS = ["complete", "accessDate", "checkFie
* @constructor
* @param {Zotero.Translator[]} translator The translator for which to run tests
* @param {String} type The type of tests to run (web, import, export, or search)
* @param {Function} [debug] A function to call to write debug output. If not present, Zotero.debug
* will be used.
* @param {Function} [debugCallback] A function to call to write debug output. If not present,
* Zotero.debug will be used.
*/
Zotero_TranslatorTester = function(translator, type, debug) {
Zotero_TranslatorTester = function(translator, type, debugCallback) {
this.type = type;
this.translator = translator;
this._debug = debug ? debug : function(obj, a, b) { Zotero.debug(a, b) };
this.output = "";
this.isSupported = this.translator.runMode === Zotero.Translator.RUN_MODE_IN_BROWSER;
this.tests = [];
this.pending = [];
@ -53,6 +156,16 @@ Zotero_TranslatorTester = function(translator, type, debug) {
this.failed = [];
this.unknown = [];
var me = this;
this._debug = function(obj, a, b) {
me.output += me.output ? "\n"+a : a;
if(debugCallback) {
debugCallback(me, a, b);
} else {
Zotero.debug(a, b);
}
};
var code = translator.code;
var testStart = code.indexOf("/** BEGIN TEST CASES **/");
var testEnd = code.indexOf("/** END TEST CASES **/");
@ -106,12 +219,29 @@ Zotero_TranslatorTester._sanitizeItem = function(item, forSave) {
} catch(e) {};
// remove fields to be ignored
for(var j=0, n=Zotero_TranslatorTester_IGNORE_FIELDS.length; j<n; j++) {
delete item[Zotero_TranslatorTester_IGNORE_FIELDS[j]];
const IGNORE_FIELDS = ["complete", "accessDate", "checkFields"];
for(var j=0, n=IGNORE_FIELDS.length; j<n; j++) {
delete item[IGNORE_FIELDS[j]];
}
return item;
};
/**
* Serializes translator tester results to JSON
*/
Zotero_TranslatorTester.prototype.serialize = function() {
return {
"translatorID":this.translator.translatorID,
"type":this.type,
"output":this.output,
"label":this.translator.label,
"isSupported":this.isSupported,
"pending":this.pending,
"failed":this.failed,
"succeeded":this.succeeded,
"unknown":this.unknown
};
};
/**
* Sets tests for this translatorTester