www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

commit 3aaebb58025cab59e285880de483a4dd63ca70b4
parent d61fdeda4cbcfce365d348252c7e0ab3582da6dd
Author: Simon Kornblith <simon@simonster.com>
Date:   Mon, 27 Feb 2012 18:50:24 -0500

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

Diffstat:
Mchrome/content/zotero/tools/testTranslators/testTranslators.css | 7+++----
Mchrome/content/zotero/tools/testTranslators/testTranslators.js | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mchrome/content/zotero/tools/testTranslators/translatorTester.js | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
3 files changed, 206 insertions(+), 36 deletions(-)

diff --git a/chrome/content/zotero/tools/testTranslators/testTranslators.css b/chrome/content/zotero/tools/testTranslators/testTranslators.css @@ -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 { diff --git a/chrome/content/zotero/tools/testTranslators/testTranslators.js b/chrome/content/zotero/tools/testTranslators/testTranslators.js @@ -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(); } /** @@ -332,6 +331,44 @@ TranslatorTestView.prototype.runTests = function(doneCallback) { } /** + * 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 */ function load(event) { @@ -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++) { diff --git a/chrome/content/zotero/tools/testTranslators/translatorTester.js b/chrome/content/zotero/tools/testTranslators/translatorTester.js @@ -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