diff options
Diffstat (limited to 'toolkit/components/telemetry/tests/unit/test_TelemetrySend.js')
-rw-r--r-- | toolkit/components/telemetry/tests/unit/test_TelemetrySend.js | 427 |
1 files changed, 0 insertions, 427 deletions
diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js b/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js deleted file mode 100644 index 88ff8cf44..000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js +++ /dev/null @@ -1,427 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ -*/ - -// This tests the public Telemetry API for submitting pings. - -"use strict"; - -Cu.import("resource://gre/modules/TelemetryController.jsm", this); -Cu.import("resource://gre/modules/TelemetrySession.jsm", this); -Cu.import("resource://gre/modules/TelemetrySend.jsm", this); -Cu.import("resource://gre/modules/TelemetryUtils.jsm", this); -Cu.import("resource://gre/modules/Services.jsm", this); -Cu.import("resource://gre/modules/Preferences.jsm", this); -Cu.import("resource://gre/modules/osfile.jsm", this); - -const PREF_TELEMETRY_SERVER = "toolkit.telemetry.server"; - -const MS_IN_A_MINUTE = 60 * 1000; - -function countPingTypes(pings) { - let countByType = new Map(); - for (let p of pings) { - countByType.set(p.type, 1 + (countByType.get(p.type) || 0)); - } - return countByType; -} - -function setPingLastModified(id, timestamp) { - const path = OS.Path.join(TelemetryStorage.pingDirectoryPath, id); - return OS.File.setDates(path, null, timestamp); -} - -// Mock out the send timer activity. -function waitForTimer() { - return new Promise(resolve => { - fakePingSendTimer((callback, timeout) => { - resolve([callback, timeout]); - }, () => {}); - }); -} - -// Allow easy faking of readable ping ids. -// This helps with debugging issues with e.g. ordering in the send logic. -function fakePingId(type, number) { - const HEAD = "93bd0011-2c8f-4e1c-bee0-"; - const TAIL = "000000000000"; - const N = String(number); - const id = HEAD + type + TAIL.slice(type.length, - N.length) + N; - fakeGeneratePingId(() => id); - return id; -} - -var checkPingsSaved = Task.async(function* (pingIds) { - let allFound = true; - for (let id of pingIds) { - const path = OS.Path.join(TelemetryStorage.pingDirectoryPath, id); - let exists = false; - try { - exists = yield OS.File.exists(path); - } catch (ex) {} - - if (!exists) { - dump("checkPingsSaved - failed to find ping: " + path + "\n"); - allFound = false; - } - } - - return allFound; -}); - -function histogramValueCount(h) { - return h.counts.reduce((a, b) => a + b); -} - -add_task(function* test_setup() { - // Trigger a proper telemetry init. - do_get_profile(true); - // Make sure we don't generate unexpected pings due to pref changes. - yield setEmptyPrefWatchlist(); - Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); -}); - -// Test the ping sending logic. -add_task(function* test_sendPendingPings() { - const TYPE_PREFIX = "test-sendPendingPings-"; - const TEST_TYPE_A = TYPE_PREFIX + "A"; - const TEST_TYPE_B = TYPE_PREFIX + "B"; - - const TYPE_A_COUNT = 20; - const TYPE_B_COUNT = 5; - - let histSuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); - let histSendTimeSuccess = Telemetry.getHistogramById("TELEMETRY_SEND_SUCCESS"); - let histSendTimeFail = Telemetry.getHistogramById("TELEMETRY_SEND_FAILURE"); - histSuccess.clear(); - histSendTimeSuccess.clear(); - histSendTimeFail.clear(); - - // Fake a current date. - let now = TelemetryUtils.truncateToDays(new Date()); - now = fakeNow(futureDate(now, 10 * 60 * MS_IN_A_MINUTE)); - - // Enable test-mode for TelemetrySend, otherwise we won't store pending pings - // before the module is fully initialized later. - TelemetrySend.setTestModeEnabled(true); - - // Submit some pings without the server and telemetry started yet. - for (let i = 0; i < TYPE_A_COUNT; ++i) { - fakePingId("a", i); - const id = yield TelemetryController.submitExternalPing(TEST_TYPE_A, {}); - yield setPingLastModified(id, now.getTime() + (i * 1000)); - } - - Assert.equal(TelemetrySend.pendingPingCount, TYPE_A_COUNT, - "Should have correct pending ping count"); - - // Submit some more pings of a different type. - now = fakeNow(futureDate(now, 5 * MS_IN_A_MINUTE)); - for (let i = 0; i < TYPE_B_COUNT; ++i) { - fakePingId("b", i); - const id = yield TelemetryController.submitExternalPing(TEST_TYPE_B, {}); - yield setPingLastModified(id, now.getTime() + (i * 1000)); - } - - Assert.equal(TelemetrySend.pendingPingCount, TYPE_A_COUNT + TYPE_B_COUNT, - "Should have correct pending ping count"); - - Assert.deepEqual(histSuccess.snapshot().counts, [0, 0, 0], - "Should not have recorded any sending in histograms yet."); - Assert.equal(histSendTimeSuccess.snapshot().sum, 0, - "Should not have recorded any sending in histograms yet."); - Assert.equal(histSendTimeFail.snapshot().sum, 0, - "Should not have recorded any sending in histograms yet."); - - // Now enable sending to the ping server. - now = fakeNow(futureDate(now, MS_IN_A_MINUTE)); - PingServer.start(); - Preferences.set(PREF_TELEMETRY_SERVER, "http://localhost:" + PingServer.port); - - let timerPromise = waitForTimer(); - yield TelemetryController.testReset(); - let [pingSendTimerCallback, pingSendTimeout] = yield timerPromise; - Assert.ok(!!pingSendTimerCallback, "Should have a timer callback"); - - // We should have received 10 pings from the first send batch: - // 5 of type B and 5 of type A, as sending is newest-first. - // The other pings should be delayed by the 10-pings-per-minute limit. - let pings = yield PingServer.promiseNextPings(10); - Assert.equal(TelemetrySend.pendingPingCount, TYPE_A_COUNT - 5, - "Should have correct pending ping count"); - PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now")); - let countByType = countPingTypes(pings); - - Assert.equal(countByType.get(TEST_TYPE_B), TYPE_B_COUNT, - "Should have received the correct amount of type B pings"); - Assert.equal(countByType.get(TEST_TYPE_A), 10 - TYPE_B_COUNT, - "Should have received the correct amount of type A pings"); - - Assert.deepEqual(histSuccess.snapshot().counts, [0, 10, 0], - "Should have recorded sending success in histograms."); - Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 10, - "Should have recorded successful send times in histograms."); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0, - "Should not have recorded any failed sending in histograms yet."); - - // As we hit the ping send limit and still have pending pings, a send tick should - // be scheduled in a minute. - Assert.ok(!!pingSendTimerCallback, "Timer callback should be set"); - Assert.equal(pingSendTimeout, MS_IN_A_MINUTE, "Send tick timeout should be correct"); - - // Trigger the next tick - we should receive the next 10 type A pings. - PingServer.resetPingHandler(); - now = fakeNow(futureDate(now, pingSendTimeout)); - timerPromise = waitForTimer(); - pingSendTimerCallback(); - [pingSendTimerCallback, pingSendTimeout] = yield timerPromise; - - pings = yield PingServer.promiseNextPings(10); - PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now")); - countByType = countPingTypes(pings); - - Assert.equal(countByType.get(TEST_TYPE_A), 10, "Should have received the correct amount of type A pings"); - - // We hit the ping send limit again and still have pending pings, a send tick should - // be scheduled in a minute. - Assert.equal(pingSendTimeout, MS_IN_A_MINUTE, "Send tick timeout should be correct"); - - // Trigger the next tick - we should receive the remaining type A pings. - PingServer.resetPingHandler(); - now = fakeNow(futureDate(now, pingSendTimeout)); - yield pingSendTimerCallback(); - - pings = yield PingServer.promiseNextPings(5); - PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now")); - countByType = countPingTypes(pings); - - Assert.equal(countByType.get(TEST_TYPE_A), 5, "Should have received the correct amount of type A pings"); - - yield TelemetrySend.testWaitOnOutgoingPings(); - PingServer.resetPingHandler(); -}); - -add_task(function* test_sendDateHeader() { - fakeNow(new Date(Date.UTC(2011, 1, 1, 11, 0, 0))); - yield TelemetrySend.reset(); - - let pingId = yield TelemetryController.submitExternalPing("test-send-date-header", {}); - let req = yield PingServer.promiseNextRequest(); - let ping = decodeRequestPayload(req); - Assert.equal(req.getHeader("Date"), "Tue, 01 Feb 2011 11:00:00 GMT", - "Telemetry should send the correct Date header with requests."); - Assert.equal(ping.id, pingId, "Should have received the correct ping id."); -}); - -// Test the backoff timeout behavior after send failures. -add_task(function* test_backoffTimeout() { - const TYPE_PREFIX = "test-backoffTimeout-"; - const TEST_TYPE_C = TYPE_PREFIX + "C"; - const TEST_TYPE_D = TYPE_PREFIX + "D"; - const TEST_TYPE_E = TYPE_PREFIX + "E"; - - let histSuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); - let histSendTimeSuccess = Telemetry.getHistogramById("TELEMETRY_SEND_SUCCESS"); - let histSendTimeFail = Telemetry.getHistogramById("TELEMETRY_SEND_FAILURE"); - - // Failing a ping send now should trigger backoff behavior. - let now = fakeNow(2010, 1, 1, 11, 0, 0); - yield TelemetrySend.reset(); - PingServer.stop(); - - histSuccess.clear(); - histSendTimeSuccess.clear(); - histSendTimeFail.clear(); - - fakePingId("c", 0); - now = fakeNow(futureDate(now, MS_IN_A_MINUTE)); - let sendAttempts = 0; - let timerPromise = waitForTimer(); - yield TelemetryController.submitExternalPing(TEST_TYPE_C, {}); - let [pingSendTimerCallback, pingSendTimeout] = yield timerPromise; - Assert.equal(TelemetrySend.pendingPingCount, 1, "Should have one pending ping."); - ++sendAttempts; - - const MAX_BACKOFF_TIMEOUT = 120 * MS_IN_A_MINUTE; - for (let timeout = 2 * MS_IN_A_MINUTE; timeout <= MAX_BACKOFF_TIMEOUT; timeout *= 2) { - Assert.ok(!!pingSendTimerCallback, "Should have received a timer callback"); - Assert.equal(pingSendTimeout, timeout, "Send tick timeout should be correct"); - - let callback = pingSendTimerCallback; - now = fakeNow(futureDate(now, pingSendTimeout)); - timerPromise = waitForTimer(); - yield callback(); - [pingSendTimerCallback, pingSendTimeout] = yield timerPromise; - ++sendAttempts; - } - - timerPromise = waitForTimer(); - yield pingSendTimerCallback(); - [pingSendTimerCallback, pingSendTimeout] = yield timerPromise; - Assert.equal(pingSendTimeout, MAX_BACKOFF_TIMEOUT, "Tick timeout should be capped"); - ++sendAttempts; - - Assert.deepEqual(histSuccess.snapshot().counts, [sendAttempts, 0, 0], - "Should have recorded sending failure in histograms."); - Assert.equal(histSendTimeSuccess.snapshot().sum, 0, - "Should not have recorded any sending success in histograms yet."); - Assert.greater(histSendTimeFail.snapshot().sum, 0, - "Should have recorded send failure times in histograms."); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), sendAttempts, - "Should have recorded send failure times in histograms."); - - // Submitting a new ping should reset the backoff behavior. - fakePingId("d", 0); - now = fakeNow(futureDate(now, MS_IN_A_MINUTE)); - timerPromise = waitForTimer(); - yield TelemetryController.submitExternalPing(TEST_TYPE_D, {}); - [pingSendTimerCallback, pingSendTimeout] = yield timerPromise; - Assert.equal(pingSendTimeout, 2 * MS_IN_A_MINUTE, "Send tick timeout should be correct"); - sendAttempts += 2; - - // With the server running again, we should send out the pending pings immediately - // when a new ping is submitted. - PingServer.start(); - TelemetrySend.setServer("http://localhost:" + PingServer.port); - fakePingId("e", 0); - now = fakeNow(futureDate(now, MS_IN_A_MINUTE)); - timerPromise = waitForTimer(); - yield TelemetryController.submitExternalPing(TEST_TYPE_E, {}); - - let pings = yield PingServer.promiseNextPings(3); - let countByType = countPingTypes(pings); - - Assert.equal(countByType.get(TEST_TYPE_C), 1, "Should have received the correct amount of type C pings"); - Assert.equal(countByType.get(TEST_TYPE_D), 1, "Should have received the correct amount of type D pings"); - Assert.equal(countByType.get(TEST_TYPE_E), 1, "Should have received the correct amount of type E pings"); - - yield TelemetrySend.testWaitOnOutgoingPings(); - Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings left"); - - Assert.deepEqual(histSuccess.snapshot().counts, [sendAttempts, 3, 0], - "Should have recorded sending failure in histograms."); - Assert.greater(histSendTimeSuccess.snapshot().sum, 0, - "Should have recorded sending success in histograms."); - Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 3, - "Should have recorded sending success in histograms."); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), sendAttempts, - "Should have recorded send failure times in histograms."); -}); - -add_task(function* test_discardBigPings() { - const TEST_PING_TYPE = "test-ping-type"; - - let histSizeExceeded = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_SEND"); - let histDiscardedSize = Telemetry.getHistogramById("TELEMETRY_DISCARDED_SEND_PINGS_SIZE_MB"); - let histSuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); - let histSendTimeSuccess = Telemetry.getHistogramById("TELEMETRY_SEND_SUCCESS"); - let histSendTimeFail = Telemetry.getHistogramById("TELEMETRY_SEND_FAILURE"); - for (let h of [histSizeExceeded, histDiscardedSize, histSuccess, histSendTimeSuccess, histSendTimeFail]) { - h.clear(); - } - - // Generate a 2MB string and create an oversized payload. - const OVERSIZED_PAYLOAD = {"data": generateRandomString(2 * 1024 * 1024)}; - - // Reset the histograms. - Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_SEND").clear(); - Telemetry.getHistogramById("TELEMETRY_DISCARDED_SEND_PINGS_SIZE_MB").clear(); - - // Submit a ping of a normal size and check that we don't count it in the histogram. - yield TelemetryController.submitExternalPing(TEST_PING_TYPE, { test: "test" }); - yield TelemetrySend.testWaitOnOutgoingPings(); - - Assert.equal(histSizeExceeded.snapshot().sum, 0, "Telemetry must report no oversized ping submitted."); - Assert.equal(histDiscardedSize.snapshot().sum, 0, "Telemetry must report no oversized pings."); - Assert.deepEqual(histSuccess.snapshot().counts, [0, 1, 0], "Should have recorded sending success."); - Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 1, "Should have recorded send success time."); - Assert.greater(histSendTimeSuccess.snapshot().sum, 0, "Should have recorded send success time."); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0, "Should not have recorded send failure time."); - - // Submit an oversized ping and check that it gets discarded. - yield TelemetryController.submitExternalPing(TEST_PING_TYPE, OVERSIZED_PAYLOAD); - yield TelemetrySend.testWaitOnOutgoingPings(); - - Assert.equal(histSizeExceeded.snapshot().sum, 1, "Telemetry must report 1 oversized ping submitted."); - Assert.equal(histDiscardedSize.snapshot().counts[2], 1, "Telemetry must report a 2MB, oversized, ping submitted."); - Assert.deepEqual(histSuccess.snapshot().counts, [0, 1, 0], "Should have recorded sending success."); - Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 1, "Should have recorded send success time."); - Assert.greater(histSendTimeSuccess.snapshot().sum, 0, "Should have recorded send success time."); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0, "Should not have recorded send failure time."); -}); - -add_task(function* test_evictedOnServerErrors() { - const TEST_TYPE = "test-evicted"; - - yield TelemetrySend.reset(); - - let histEvicted = Telemetry.getHistogramById("TELEMETRY_PING_EVICTED_FOR_SERVER_ERRORS"); - let histSuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); - let histSendTimeSuccess = Telemetry.getHistogramById("TELEMETRY_SEND_SUCCESS"); - let histSendTimeFail = Telemetry.getHistogramById("TELEMETRY_SEND_FAILURE"); - for (let h of [histEvicted, histSuccess, histSendTimeSuccess, histSendTimeFail]) { - h.clear(); - } - - // Write a custom ping handler which will return 403. This will trigger ping eviction - // on client side. - PingServer.registerPingHandler((req, res) => { - res.setStatusLine(null, 403, "Forbidden"); - res.processAsync(); - res.finish(); - }); - - // Clear the histogram and submit a ping. - let pingId = yield TelemetryController.submitExternalPing(TEST_TYPE, {}); - yield TelemetrySend.testWaitOnOutgoingPings(); - - Assert.equal(histEvicted.snapshot().sum, 1, - "Telemetry must report a ping evicted due to server errors"); - Assert.deepEqual(histSuccess.snapshot().counts, [0, 1, 0]); - Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 1); - Assert.greater(histSendTimeSuccess.snapshot().sum, 0); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0); - - // The ping should not be persisted. - yield Assert.rejects(TelemetryStorage.loadPendingPing(pingId), "The ping must not be persisted."); - - // Reset the ping handler and submit a new ping. - PingServer.resetPingHandler(); - pingId = yield TelemetryController.submitExternalPing(TEST_TYPE, {}); - - let ping = yield PingServer.promiseNextPings(1); - Assert.equal(ping[0].id, pingId, "The correct ping must be received"); - - // We should not have updated the error histogram. - yield TelemetrySend.testWaitOnOutgoingPings(); - Assert.equal(histEvicted.snapshot().sum, 1, "Telemetry must report only one ping evicted due to server errors"); - Assert.deepEqual(histSuccess.snapshot().counts, [0, 2, 0]); - Assert.equal(histogramValueCount(histSendTimeSuccess.snapshot()), 2); - Assert.equal(histogramValueCount(histSendTimeFail.snapshot()), 0); -}); - -// Test that the current, non-persisted pending pings are properly saved on shutdown. -add_task(function* test_persistCurrentPingsOnShutdown() { - const TEST_TYPE = "test-persistCurrentPingsOnShutdown"; - const PING_COUNT = 5; - yield TelemetrySend.reset(); - PingServer.stop(); - Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings yet"); - - // Submit new pings that shouldn't be persisted yet. - let ids = []; - for (let i=0; i<5; ++i) { - ids.push(fakePingId("f", i)); - TelemetryController.submitExternalPing(TEST_TYPE, {}); - } - - Assert.equal(TelemetrySend.pendingPingCount, PING_COUNT, "Should have the correct pending ping count"); - - // Triggering a shutdown should persist the pings. - yield TelemetrySend.shutdown(); - Assert.ok((yield checkPingsSaved(ids)), "All pending pings should have been persisted"); - - // After a restart the pings should have been found when scanning. - yield TelemetrySend.reset(); - Assert.equal(TelemetrySend.pendingPingCount, PING_COUNT, "Should have the correct pending ping count"); -}); |