author | Benjamin Smedberg <benjamin@smedbergs.us> |
Mon, 30 Mar 2015 17:48:11 -0400 | |
changeset 248036 | 6034dc30409f9da00973f116b9c0f6ae14fa14b8 |
parent 248035 | 01a6f68c4e53ed187a4bb059a48b324d2260ee50 |
child 248037 | 986f641d87e4115fe9a0a909c7aa698d3b2ec9e0 |
push id | 60888 |
push user | kwierso@gmail.com |
push date | Thu, 11 Jun 2015 01:38:38 +0000 |
treeherder | mozilla-inbound@39e638ed06bf [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | gfritzsche |
bugs | 1121013 |
milestone | 41.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/toolkit/components/crashes/CrashManager.jsm +++ b/toolkit/components/crashes/CrashManager.jsm @@ -1,24 +1,26 @@ /* 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/. */ "use strict"; const {classes: Cc, interfaces: Ci, utils: Cu} = Components; +const myScope = this; Cu.import("resource://gre/modules/Log.jsm", this); -Cu.import("resource://gre/modules/osfile.jsm", this) +Cu.import("resource://gre/modules/osfile.jsm", this); Cu.import("resource://gre/modules/Promise.jsm", this); Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/Task.jsm", this); Cu.import("resource://gre/modules/Timer.jsm", this); Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); Cu.import("resource://services-common/utils.js", this); +Cu.import("resource://gre/modules/TelemetryController.jsm"); this.EXPORTED_SYMBOLS = [ "CrashManager", ]; /** * How long to wait after application startup before crash event files are * automatically aggregated. @@ -524,16 +526,42 @@ this.CrashManager.prototype = Object.fre let crashID = lines[0]; let metadata = {}; for (let i = 1; i < lines.length; i++) { let [key, val] = lines[i].split("="); metadata[key] = val; } store.addCrash(this.PROCESS_TYPE_MAIN, this.CRASH_TYPE_CRASH, crashID, date, metadata); + + // If we have a saved environment, use it. Otherwise report + // the current environment. + let crashEnvironment = null; + let reportMeta = Cu.cloneInto(metadata, myScope); + if ('TelemetryEnvironment' in reportMeta) { + try { + crashEnvironment = JSON.parse(reportMeta.TelemetryEnvironment); + } catch(e) { + Cu.reportError(e); + } + delete reportMeta.TelemetryEnvironment; + } + TelemetryController.submitExternalPing("crash", + { + version: 1, + crashDate: date.toISOString().slice(0, 10), // YYYY-MM-DD + metadata: reportMeta, + hasCrashEnvironment: (crashEnvironment !== null), + }, + { + retentionDays: 180, + addClientId: true, + addEnvironment: true, + overrideEnvironment: crashEnvironment, + }); break; case "crash.submission.1": if (lines.length == 3) { let [crashID, result, remoteID] = lines; store.addCrash(this.PROCESS_TYPE_MAIN, this.CRASH_TYPE_CRASH, crashID, date); @@ -768,24 +796,20 @@ CrashStore.prototype = Object.freeze({ // days stored in the payload matches up to actual data. let actualCounts = new Map(); // In the past, submissions were stored as separate crash records // with an id of e.g. "someID-submission". If we find IDs ending // with "-submission", we will need to convert the data to be stored // as actual submissions. // - // TODO: The old way of storing submissions was used from FF33 - FF34. - // We should drop the conversion code after a few releases. See bug - // 1056157. - let hasSubmissionsStoredAsCrashes = false; - + // The old way of storing submissions was used from FF33 - FF34. We + // drop this old data on the floor. for (let id in data.crashes) { if (id.endsWith("-submission")) { - hasSubmissionsStoredAsCrashes = true; continue; } let crash = data.crashes[id]; let denormalized = this._denormalize(crash); denormalized.submissions = new Map(); if (crash.submissions) { @@ -806,42 +830,16 @@ CrashStore.prototype = Object.freeze({ if (denormalized.metadata && denormalized.metadata.OOMAllocationSize) { let oomKey = key + "-oom"; actualCounts.set(oomKey, (actualCounts.get(oomKey) || 0) + 1); } } - if (hasSubmissionsStoredAsCrashes) { - for (let id in data.crashes) { - if (!id.endsWith("-submission")) { - continue; - } - - // This type of record will contain e.g.: - // { - // "id": "crash1-submission", - // "type": "main-crash-submission-succeeded", - // "crashDate": "...", - // } - let submissionData = this._denormalize(data.crashes[id]); - - let crashID = id.replace(/-submission$/, ""); - let result = submissionData.type.endsWith("-succeeded") ? - CrashManager.prototype.SUBMISSION_RESULT_OK : - CrashManager.prototype.SUBMISSION_RESULT_FAILED; - - this.addSubmissionAttempt(crashID, "converted", - submissionData.crashDate); - this.addSubmissionResult(crashID, "converted", - submissionData.crashDate, result); - } - } - // The validation in this loop is arguably not necessary. We perform // it as a defense against unknown bugs. for (let dayKey in data.countsByDay) { let day = parseInt(dayKey, 10); for (let type in data.countsByDay[day]) { this._ensureCountsForDay(day); let count = data.countsByDay[day][type]; @@ -1155,73 +1153,69 @@ CrashStore.prototype = Object.freeze({ crashes.push(crash); } } return crashes; }, /** - * Obtain a particular crash submission from its ID. - * - * @return undefined | submission object - */ - getSubmission: function (crashID, submissionID) { - let crash = this._data.crashes.get(crashID); - if (!crash || !submissionID) { - return undefined; - } - - return crash.submissions.get(submissionID); - }, - - /** * Ensure the submission record is present in storage. + * @returns [submission, crash] */ _ensureSubmissionRecord: function (crashID, submissionID) { let crash = this._data.crashes.get(crashID); if (!crash || !submissionID) { return null; } if (!crash.submissions.has(submissionID)) { crash.submissions.set(submissionID, { requestDate: null, responseDate: null, result: null, }); } - return crash.submissions.get(submissionID); + return [crash.submissions.get(submissionID), crash]; }, /** * @return boolean True if the attempt was recorded. */ addSubmissionAttempt: function (crashID, submissionID, date) { - let submission = this._ensureSubmissionRecord(crashID, submissionID); + let [submission, crash] = + this._ensureSubmissionRecord(crashID, submissionID); if (!submission) { return false; } submission.requestDate = date; + Services.telemetry.getKeyedHistogramById("PROCESS_CRASH_SUBMIT_ATTEMPT") + .add(crash.type, 1); return true; }, /** * @return boolean True if the response was recorded. */ addSubmissionResult: function (crashID, submissionID, date, result) { - let submission = this.getSubmission(crashID, submissionID); + let crash = this._data.crashes.get(crashID); + if (!crash || !submissionID) { + return false; + } + let submission = crash.submissions.get(submissionID); if (!submission) { return false; } submission.responseDate = date; submission.result = result; + Services.telemetry.getKeyedHistogramById("PROCESS_CRASH_SUBMIT_SUCCESS") + .add(crash.type, result == "ok"); return true; }, /** * @return boolean True if the classifications were set. */ setCrashClassifications: function (crashID, classifications) { let crash = this._data.crashes.get(crashID);
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js +++ b/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js @@ -4,28 +4,31 @@ "use strict"; const {classes: Cc, interfaces: Ci, utils: Cu} = Components; let bsp = Cu.import("resource://gre/modules/CrashManager.jsm", this); Cu.import("resource://gre/modules/Promise.jsm", this); Cu.import("resource://gre/modules/Task.jsm", this); Cu.import("resource://gre/modules/osfile.jsm", this); +Cu.import("resource://gre/modules/TelemetryEnvironment.jsm", this); Cu.import("resource://testing-common/CrashManagerTest.jsm", this); +Cu.import("resource://testing-common/TelemetryArchiveTesting.jsm", this); const DUMMY_DATE = new Date(Date.now() - 10 * 24 * 60 * 60 * 1000); DUMMY_DATE.setMilliseconds(0); const DUMMY_DATE_2 = new Date(Date.now() - 20 * 24 * 60 * 60 * 1000); DUMMY_DATE_2.setMilliseconds(0); function run_test() { do_get_profile(); configureLogging(); + TelemetryArchiveTesting.setup(); run_next_test(); } add_task(function* test_constructor_ok() { let m = new CrashManager({ pendingDumpsDir: "/foo", submittedDumpsDir: "/bar", eventsDirs: [], @@ -202,28 +205,69 @@ add_task(function* test_schedule_mainten yield m.scheduleMaintenance(25); let crashes = yield m.getCrashes(); Assert.equal(crashes.length, 1); Assert.equal(crashes[0].id, "id1"); }); add_task(function* test_main_crash_event_file() { + let ac = new TelemetryArchiveTesting.Checker(); + yield ac.promiseInit(); + let theEnvironment = TelemetryEnvironment.currentEnvironment; + let m = yield getManager(); - yield m.createEventsFile("1", "crash.main.2", DUMMY_DATE, "id1\nk1=v1\nk2=v2"); + yield m.createEventsFile("1", "crash.main.2", DUMMY_DATE, "id1\nk1=v1\nk2=v2\nTelemetryEnvironment=" + JSON.stringify(theEnvironment)); let count = yield m.aggregateEventsFiles(); Assert.equal(count, 1); let crashes = yield m.getCrashes(); Assert.equal(crashes.length, 1); Assert.equal(crashes[0].id, "id1"); Assert.equal(crashes[0].type, "main-crash"); - Assert.deepEqual(crashes[0].metadata, { k1: "v1", k2: "v2"}); + Assert.equal(crashes[0].metadata.k1, "v1"); + Assert.equal(crashes[0].metadata.k2, "v2"); + Assert.ok(crashes[0].metadata.TelemetryEnvironment); + Assert.equal(Object.getOwnPropertyNames(crashes[0].metadata).length, 3); Assert.deepEqual(crashes[0].crashDate, DUMMY_DATE); + let found = yield ac.promiseFindPing("crash", [ + [["payload", "hasCrashEnvironment"], true], + [["payload", "metadata", "k1"], "v1"], + ]); + Assert.ok(found, "Telemetry ping submitted for found crash"); + Assert.deepEqual(found.environment, theEnvironment, "The saved environment should be present"); + + count = yield m.aggregateEventsFiles(); + Assert.equal(count, 0); +}); + +add_task(function* test_main_crash_event_file_noenv() { + let ac = new TelemetryArchiveTesting.Checker(); + yield ac.promiseInit(); + + let m = yield getManager(); + yield m.createEventsFile("1", "crash.main.2", DUMMY_DATE, "id1\nk1=v3\nk2=v2"); + let count = yield m.aggregateEventsFiles(); + Assert.equal(count, 1); + + let crashes = yield m.getCrashes(); + Assert.equal(crashes.length, 1); + Assert.equal(crashes[0].id, "id1"); + Assert.equal(crashes[0].type, "main-crash"); + Assert.deepEqual(crashes[0].metadata, { k1: "v3", k2: "v2"}); + Assert.deepEqual(crashes[0].crashDate, DUMMY_DATE); + + let found = yield ac.promiseFindPing("crash", [ + [["payload", "hasCrashEnvironment"], false], + [["payload", "metadata", "k1"], "v3"], + ]); + Assert.ok(found, "Telemetry ping submitted for found crash"); + Assert.ok(found.environment, "There is an environment"); + count = yield m.aggregateEventsFiles(); Assert.equal(count, 0); }); add_task(function* test_crash_submission_event_file() { let m = yield getManager(); yield m.createEventsFile("1", "crash.main.2", DUMMY_DATE, "crash1"); yield m.createEventsFile("1-submission", "crash.submission.1", DUMMY_DATE_2,
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_store.js +++ b/toolkit/components/crashes/tests/xpcshell/test_crash_store.js @@ -487,97 +487,52 @@ add_task(function* test_high_water() { Assert.equal(s._countsByDay.get(day1). get(PROCESS_TYPE_PLUGIN + "-" + CRASH_TYPE_CRASH), s.HIGH_WATER_DAILY_THRESHOLD + 1); Assert.equal(s._countsByDay.get(day1). get(PROCESS_TYPE_PLUGIN + "-" + CRASH_TYPE_HANG), s.HIGH_WATER_DAILY_THRESHOLD + 1); }); -add_task(function* test_getSubmission() { - let s = yield getStore(); - - Assert.equal(s.getSubmission("crash1", "sub1"), undefined); - Assert.ok(s.addCrash(PROCESS_TYPE_MAIN, CRASH_TYPE_CRASH, "crash1", - DUMMY_DATE)); - Assert.equal(s.getSubmission("crash1", "sub1"), undefined); - Assert.ok(s.addSubmissionAttempt("crash1", "sub1", DUMMY_DATE)); - Assert.ok(s.getSubmission("crash1", "sub1")); -}); - add_task(function* test_addSubmission() { let s = yield getStore(); Assert.ok(s.addCrash(PROCESS_TYPE_MAIN, CRASH_TYPE_CRASH, "crash1", DUMMY_DATE)); Assert.ok(s.addSubmissionAttempt("crash1", "sub1", DUMMY_DATE)); - let submission = s.getSubmission("crash1", "sub1"); + let crash = s.getCrash("crash1"); + let submission = crash.submissions.get("sub1"); Assert.ok(!!submission); Assert.equal(submission.requestDate.getTime(), DUMMY_DATE.getTime()); Assert.equal(submission.responseDate, null); Assert.equal(submission.result, null); Assert.ok(s.addSubmissionResult("crash1", "sub1", DUMMY_DATE_2, SUBMISSION_RESULT_FAILED)); - submission = s.getSubmission("crash1", "sub1"); + crash = s.getCrash("crash1"); + Assert.equal(crash.submissions.size, 1); + submission = crash.submissions.get("sub1"); Assert.ok(!!submission); Assert.equal(submission.requestDate.getTime(), DUMMY_DATE.getTime()); Assert.equal(submission.responseDate.getTime(), DUMMY_DATE_2.getTime()); Assert.equal(submission.result, SUBMISSION_RESULT_FAILED); Assert.ok(s.addSubmissionAttempt("crash1", "sub2", DUMMY_DATE)); Assert.ok(s.addSubmissionResult("crash1", "sub2", DUMMY_DATE_2, SUBMISSION_RESULT_OK)); - submission = s.getSubmission("crash1", "sub2"); + Assert.equal(crash.submissions.size, 2); + submission = crash.submissions.get("sub2"); Assert.ok(!!submission); Assert.equal(submission.result, SUBMISSION_RESULT_OK); }); -add_task(function* test_convertSubmissionsStoredAsCrashes() { - let s = yield getStore(); - - let addSubmissionAsCrash = (processType, crashType, succeeded, id, date) => { - id = id + "-submission"; - let process = processType + "-" + crashType + "-submission"; - let submissionType = succeeded ? "succeeded" : "failed"; - return s.addCrash(process, submissionType, id, date); - }; - - Assert.ok(s.addCrash(PROCESS_TYPE_MAIN, CRASH_TYPE_CRASH, "crash1", - new Date())); - Assert.ok(addSubmissionAsCrash(PROCESS_TYPE_MAIN, CRASH_TYPE_CRASH, true, - "crash1", DUMMY_DATE)); - - Assert.ok(s.addCrash(PROCESS_TYPE_PLUGIN, CRASH_TYPE_HANG, "hang1", - new Date())); - Assert.ok(addSubmissionAsCrash(PROCESS_TYPE_PLUGIN, CRASH_TYPE_HANG, false, - "hang1", DUMMY_DATE_2)); - - Assert.equal(s.crashes.length, 4); - yield s.save(); - yield s.load(); - Assert.equal(s.crashes.length, 2); - - let submission = s.getSubmission("crash1", "converted"); - Assert.ok(!!submission); - Assert.equal(submission.result, SUBMISSION_RESULT_OK); - Assert.equal(submission.requestDate.getTime(), DUMMY_DATE.getTime()); - Assert.equal(submission.responseDate.getTime(), DUMMY_DATE.getTime()); - - submission = s.getSubmission("hang1", "converted"); - Assert.ok(!!submission); - Assert.equal(submission.result, SUBMISSION_RESULT_FAILED); - Assert.equal(submission.requestDate.getTime(), DUMMY_DATE_2.getTime()); - Assert.equal(submission.responseDate.getTime(), DUMMY_DATE_2.getTime()); -}); - add_task(function* test_setCrashClassification() { let s = yield getStore(); Assert.ok(s.addCrash(PROCESS_TYPE_MAIN, CRASH_TYPE_CRASH, "crash1", new Date())); let classifications = s.crashes[0].classifications; Assert.ok(!!classifications); Assert.equal(classifications.length, 0);
--- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -7620,16 +7620,30 @@ "description": "Counts of plugin/content process abnormal shutdown, whether or not a crash report was available." }, "SUBPROCESS_CRASHES_WITH_DUMP": { "expires_in_version": "never", "kind": "count", "keyed": true, "description": "Counts of plugin and content process crashes which are reported with a crash dump." }, + "PROCESS_CRASH_SUBMIT_ATTEMPT": { + "expires_in_version": "never", + "kind": "count", + "keyed": true, + "releaseChannelCollection": "opt-out", + "description": "An attempt to submit a crash. Keyed on the CrashManager Crash.type." + }, + "PROCESS_CRASH_SUBMIT_SUCCESS": { + "expires_in_version": "never", + "kind": "boolean", + "keyed": true, + "releaseChannelCollection": "opt-out", + "description": "The submission status when main/plugin/content crashes are submitted. 1 is success, 0 is failure. Keyed on the CrashManager Crash.type." + }, "STUMBLER_TIME_BETWEEN_UPLOADS_SEC": { "expires_in_version": "45", "kind": "exponential", "n_buckets": 50, "high": 259200, "description": "Stumbler: The time in seconds between uploads." }, "STUMBLER_VOLUME_BYTES_UPLOADED_PER_SEC": {
--- a/toolkit/components/telemetry/TelemetryEnvironment.jsm +++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm @@ -895,23 +895,23 @@ EnvironmentCache.prototype = { }, /** * Get the build data in object form. * @return Object containing the build data. */ _getBuild: function () { let buildData = { - applicationId: Services.appinfo.ID, - applicationName: Services.appinfo.name, + applicationId: Services.appinfo.ID || null, + applicationName: Services.appinfo.name || null, architecture: Services.sysinfo.get("arch"), - buildId: Services.appinfo.appBuildID, - version: Services.appinfo.version, - vendor: Services.appinfo.vendor, - platformVersion: Services.appinfo.platformVersion, + buildId: Services.appinfo.appBuildID || null, + version: Services.appinfo.version || null, + vendor: Services.appinfo.vendor || null, + platformVersion: Services.appinfo.platformVersion || null, xpcomAbi: Services.appinfo.XPCOMABI, hotfixVersion: Preferences.get(PREF_HOTFIX_LASTVERSION, null), }; // Add |architecturesInBinary| only for Mac Universal builds. if ("@mozilla.org/xpcom/mac-utils;1" in Cc) { let macUtils = Cc["@mozilla.org/xpcom/mac-utils;1"].getService(Ci.nsIMacUtils); if (macUtils && macUtils.isUniversalBinary) {
new file mode 100644 --- /dev/null +++ b/toolkit/components/telemetry/docs/crash-ping.rst @@ -0,0 +1,24 @@ + +"crash" ping +============ + +This ping is captured after the main Firefox process crashes, whether or not the crash report is submitted to crash-stats.mozilla.org. It includes non-identifying metadata about the crash. + +The environment block that is sent with this ping varies: if Firefox was running long enough to record the environment block before the crash, then the environment at the time of the crash will be recorded and ``hasCrashEnvironment`` will be true. If Firefox crashed before the environment was recorded, ``hasCrashEnvironment`` will be false and the recorded environment will be the environment at time of submission. + +The client ID is submitted with this ping. + +Structure:: + + { + version: 1, + type: "crash", + ... common ping data + clientId: <UUID>, + environment: { ... }, + payload: { + crashDate: "YYYY-MM-DD", + metadata: {...}, // Annotations saved while Firefox was running. See nsExceptionHandler.cpp for more information + hasCrashEnvironment: bool + } + }
--- a/toolkit/components/telemetry/docs/pings.rst +++ b/toolkit/components/telemetry/docs/pings.rst @@ -24,17 +24,18 @@ The telemetry server team is working tow * `2XX` - success, don't resubmit * `4XX` - there was some problem with the request - the client should not try to resubmit as it would just receive the same response * `5XX` - there was a server-side error, the client should try to resubmit later Ping types ========== * :doc:`main <main-ping>` - contains the information collected by Telemetry (Histograms, hang stacks, ...) -* :doc:`saved-session <main-ping>` - has the same format as a main ping, but it contains the *"classic"* Telemetry payload with measurements covering the whole browser session. This is only a separate type to make storage of saved-session easier server-side. +* :doc:`saved-session <main-ping>` - has the same format as a main ping, but it contains the *"classic"* Telemetry payload with measurements covering the whole browser session. This is only a separate type to make storage of saved-session easier server-side. This is temporary and will be removed soon. +* :doc:`crash <crash-ping>` - a ping that is captured and sent after Firefox crashes. * ``activation`` - *planned* - sent right after installation or profile creation * ``upgrade`` - *planned* - sent right after an upgrade * ``deletion`` - *planned* - on opt-out we may have to tell the server to delete user data Archiving ========= When archiving is enabled through the relative preference, pings submitted to ``TelemetryController`` are also stored locally in the user profile directory, in `<profile-dir>/datareporting/archived`.
--- a/toolkit/components/telemetry/moz.build +++ b/toolkit/components/telemetry/moz.build @@ -41,16 +41,20 @@ EXTRA_JS_MODULES += [ ] EXTRA_PP_JS_MODULES += [ 'TelemetryController.jsm', 'TelemetryEnvironment.jsm', 'TelemetrySession.jsm', ] +TESTING_JS_MODULES += [ + 'tests/unit/TelemetryArchiveTesting.jsm', +] + FAIL_ON_WARNINGS = True include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' GENERATED_FILES = [ 'TelemetryHistogramData.inc',
new file mode 100644 --- /dev/null +++ b/toolkit/components/telemetry/tests/unit/TelemetryArchiveTesting.jsm @@ -0,0 +1,86 @@ +const {utils: Cu} = Components; +Cu.import("resource://gre/modules/TelemetryArchive.jsm"); +Cu.import("resource://testing-common/Assert.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/TelemetryController.jsm"); + +this.EXPORTED_SYMBOLS = [ + "TelemetryArchiveTesting", +]; + +function checkForProperties(ping, expected) { + for (let [props, val] of expected) { + let test = ping; + for (let prop of props) { + test = test[prop]; + if (test === undefined) { + return false; + } + } + if (test !== val) { + return false; + } + } + return true; +} + +/** + * A helper object that allows test code to check whether a telemetry ping + * was properly saved. To use, first initialize to collect the starting pings + * and then check for new ping data. + */ +function Checker() { +} +Checker.prototype = { + promiseInit: function() { + this._pingMap = new Map(); + return TelemetryArchive.promiseArchivedPingList().then((plist) => { + for (let ping of plist) { + this._pingMap.set(ping.id, ping); + } + }); + }, + + /** + * Find and return a new ping with certain properties. + * + * @param expected: an array of [['prop'...], 'value'] to check + * For example: + * [ + * [['environment', 'build', 'applicationId'], '20150101010101'], + * [['version'], 1], + * [['metadata', 'OOMAllocationSize'], 123456789], + * ] + * @returns a matching ping if found, or null + */ + promiseFindPing: Task.async(function*(type, expected) { + let candidates = []; + let plist = yield TelemetryArchive.promiseArchivedPingList(); + for (let ping of plist) { + if (this._pingMap.has(ping.id)) { + continue; + } + if (ping.type == type) { + candidates.push(ping); + } + } + + for (let candidate of candidates) { + let ping = yield TelemetryArchive.promiseArchivedPingById(candidate.id); + if (checkForProperties(ping, expected)) { + return ping; + } + } + return null; + }), +}; + +const TelemetryArchiveTesting = { + setup: function() { + Services.prefs.setCharPref("toolkit.telemetry.log.level", "Trace"); + Services.prefs.setBoolPref("toolkit.telemetry.archive.enabled", true); + }, + + Checker: Checker, +};