/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; var tmp = {}; Cu.import("resource:///modules/translation/Translation.jsm", tmp); var {Translation, TranslationTelemetry} = tmp; const Telemetry = Services.telemetry; var MetricsChecker = { HISTOGRAMS: { OPPORTUNITIES : Services.telemetry.getHistogramById("TRANSLATION_OPPORTUNITIES"), OPPORTUNITIES_BY_LANG : Services.telemetry.getKeyedHistogramById("TRANSLATION_OPPORTUNITIES_BY_LANGUAGE"), PAGES : Services.telemetry.getHistogramById("TRANSLATED_PAGES"), PAGES_BY_LANG : Services.telemetry.getKeyedHistogramById("TRANSLATED_PAGES_BY_LANGUAGE"), CHARACTERS : Services.telemetry.getHistogramById("TRANSLATED_CHARACTERS"), DENIED : Services.telemetry.getHistogramById("DENIED_TRANSLATION_OFFERS"), AUTO_REJECTED : Services.telemetry.getHistogramById("AUTO_REJECTED_TRANSLATION_OFFERS"), SHOW_ORIGINAL : Services.telemetry.getHistogramById("REQUESTS_OF_ORIGINAL_CONTENT"), TARGET_CHANGES : Services.telemetry.getHistogramById("CHANGES_OF_TARGET_LANGUAGE"), DETECTION_CHANGES : Services.telemetry.getHistogramById("CHANGES_OF_DETECTED_LANGUAGE"), SHOW_UI : Services.telemetry.getHistogramById("SHOULD_TRANSLATION_UI_APPEAR"), DETECT_LANG : Services.telemetry.getHistogramById("SHOULD_AUTO_DETECT_LANGUAGE"), }, reset: function() { for (let i of Object.keys(this.HISTOGRAMS)) { this.HISTOGRAMS[i].clear(); } this.updateMetrics(); }, updateMetrics: function () { this._metrics = { opportunitiesCount: this.HISTOGRAMS.OPPORTUNITIES.snapshot().sum || 0, pageCount: this.HISTOGRAMS.PAGES.snapshot().sum || 0, charCount: this.HISTOGRAMS.CHARACTERS.snapshot().sum || 0, deniedOffers: this.HISTOGRAMS.DENIED.snapshot().sum || 0, autoRejectedOffers: this.HISTOGRAMS.AUTO_REJECTED.snapshot().sum || 0, showOriginal: this.HISTOGRAMS.SHOW_ORIGINAL.snapshot().sum || 0, detectedLanguageChangedBefore: this.HISTOGRAMS.DETECTION_CHANGES.snapshot().counts[1] || 0, detectedLanguageChangeAfter: this.HISTOGRAMS.DETECTION_CHANGES.snapshot().counts[0] || 0, targetLanguageChanged: this.HISTOGRAMS.TARGET_CHANGES.snapshot().sum || 0, showUI: this.HISTOGRAMS.SHOW_UI.snapshot().sum || 0, detectLang: this.HISTOGRAMS.DETECT_LANG.snapshot().sum || 0, // Metrics for Keyed histograms are estimated below. opportunitiesCountByLang: {}, pageCountByLang: {} }; let opportunities = this.HISTOGRAMS.OPPORTUNITIES_BY_LANG.snapshot(); let pages = this.HISTOGRAMS.PAGES_BY_LANG.snapshot(); for (let source of Translation.supportedSourceLanguages) { this._metrics.opportunitiesCountByLang[source] = opportunities[source] ? opportunities[source].sum : 0; for (let target of Translation.supportedTargetLanguages) { if (source === target) continue; let key = source + " -> " + target; this._metrics.pageCountByLang[key] = pages[key] ? pages[key].sum : 0; } } }, /** * A recurrent loop for making assertions about collected metrics. */ _assertionLoop: function (prevMetrics, metrics, additions) { for (let metric of Object.keys(additions)) { let addition = additions[metric]; // Allows nesting metrics. Useful for keyed histograms. if (typeof addition === 'object') { this._assertionLoop(prevMetrics[metric], metrics[metric], addition); continue; } Assert.equal(prevMetrics[metric] + addition, metrics[metric]); } }, checkAdditions: function (additions) { let prevMetrics = this._metrics; this.updateMetrics(); this._assertionLoop(prevMetrics, this._metrics, additions); } }; function getInfobarElement(browser, anonid) { let notif = browser.translationUI .notificationBox.getNotificationWithValue("translation"); return notif._getAnonElt(anonid); } var offerTranslationFor = Task.async(function*(text, from) { // Create some content to translate. const dataUrl = "data:text/html;charset=utf-8," + text; let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, dataUrl); let browser = gBrowser.getBrowserForTab(tab); // Send a translation offer. Translation.documentStateReceived(browser, {state: Translation.STATE_OFFER, originalShown: true, detectedLanguage: from}); return tab; }); var acceptTranslationOffer = Task.async(function*(tab) { let browser = tab.linkedBrowser; getInfobarElement(browser, "translate").doCommand(); yield waitForMessage(browser, "Translation:Finished"); }); var translate = Task.async(function*(text, from, closeTab = true) { let tab = yield offerTranslationFor(text, from); yield acceptTranslationOffer(tab); if (closeTab) { gBrowser.removeTab(tab); return null; } return tab; }); function waitForMessage({messageManager}, name) { return new Promise(resolve => { messageManager.addMessageListener(name, function onMessage() { messageManager.removeMessageListener(name, onMessage); resolve(); }); }); } function simulateUserSelectInMenulist(menulist, value) { menulist.value = value; menulist.doCommand(); } add_task(function* setup() { const setupPrefs = prefs => { let prefsBackup = {}; for (let p of prefs) { prefsBackup[p] = Services.prefs.setBoolPref; Services.prefs.setBoolPref(p, true); } return prefsBackup; }; const restorePrefs = (prefs, backup) => { for (let p of prefs) { Services.prefs.setBoolPref(p, backup[p]); } }; const prefs = [ "toolkit.telemetry.enabled", "browser.translation.detectLanguage", "browser.translation.ui.show" ]; let prefsBackup = setupPrefs(prefs); let oldCanRecord = Telemetry.canRecordExtended; Telemetry.canRecordExtended = true; registerCleanupFunction(() => { restorePrefs(prefs, prefsBackup); Telemetry.canRecordExtended = oldCanRecord; }); // Reset histogram metrics. MetricsChecker.reset(); }); add_task(function* test_telemetry() { // Translate a page. yield translate("