toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 /* Any copyright is dedicated to the Public Domain.
michael@0 2 http://creativecommons.org/publicdomain/zero/1.0/
michael@0 3
michael@0 4 /**
michael@0 5 * This test case populates the profile with some fake stored
michael@0 6 * pings, and checks that:
michael@0 7 *
michael@0 8 * 1) Pings that are considered "expired" are deleted and never sent.
michael@0 9 * 2) Pings that are considered "overdue" trigger a send of all
michael@0 10 * overdue and recent pings.
michael@0 11 */
michael@0 12
michael@0 13 "use strict"
michael@0 14
michael@0 15 const Cc = Components.classes;
michael@0 16 const Ci = Components.interfaces;
michael@0 17 const Cr = Components.results;
michael@0 18 const Cu = Components.utils;
michael@0 19
michael@0 20 Cu.import("resource://gre/modules/Services.jsm", this);
michael@0 21 Cu.import("resource://testing-common/httpd.js", this);
michael@0 22 Cu.import("resource://gre/modules/Promise.jsm", this);
michael@0 23 Cu.import("resource://gre/modules/TelemetryFile.jsm", this);
michael@0 24 Cu.import("resource://gre/modules/TelemetryPing.jsm", this);
michael@0 25 Cu.import("resource://gre/modules/Task.jsm", this);
michael@0 26 let {OS: {File, Path, Constants}} = Cu.import("resource://gre/modules/osfile.jsm", {});
michael@0 27
michael@0 28 // We increment TelemetryFile's MAX_PING_FILE_AGE and
michael@0 29 // OVERDUE_PING_FILE_AGE by 1 minute so that our test pings exceed
michael@0 30 // those points in time, even taking into account file system imprecision.
michael@0 31 const ONE_MINUTE_MS = 60 * 1000;
michael@0 32 const EXPIRED_PING_FILE_AGE = TelemetryFile.MAX_PING_FILE_AGE + ONE_MINUTE_MS;
michael@0 33 const OVERDUE_PING_FILE_AGE = TelemetryFile.OVERDUE_PING_FILE_AGE + ONE_MINUTE_MS;
michael@0 34
michael@0 35 const PING_SAVE_FOLDER = "saved-telemetry-pings";
michael@0 36 const PING_TIMEOUT_LENGTH = 5000;
michael@0 37 const EXPIRED_PINGS = 5;
michael@0 38 const OVERDUE_PINGS = 6;
michael@0 39 const RECENT_PINGS = 4;
michael@0 40
michael@0 41 const TOTAL_EXPECTED_PINGS = OVERDUE_PINGS + RECENT_PINGS;
michael@0 42
michael@0 43 let gHttpServer = new HttpServer();
michael@0 44 let gCreatedPings = 0;
michael@0 45 let gSeenPings = 0;
michael@0 46
michael@0 47 /**
michael@0 48 * Creates some TelemetryPings for the current session and
michael@0 49 * saves them to disk. Each ping gets a unique ID slug based on
michael@0 50 * an incrementor.
michael@0 51 *
michael@0 52 * @param aNum the number of pings to create.
michael@0 53 * @param aAge the age in milliseconds to offset from now. A value
michael@0 54 * of 10 would make the ping 10ms older than now, for
michael@0 55 * example.
michael@0 56 * @returns Promise
michael@0 57 * @resolve an Array with the created pings.
michael@0 58 */
michael@0 59 function createSavedPings(aNum, aAge) {
michael@0 60 return Task.spawn(function*(){
michael@0 61 // Create a TelemetryPing service that we can generate payloads from.
michael@0 62 // Luckily, the TelemetryPing constructor does nothing that we need to
michael@0 63 // clean up.
michael@0 64 let pings = [];
michael@0 65 let age = Date.now() - aAge;
michael@0 66
michael@0 67 for (let i = 0; i < aNum; ++i) {
michael@0 68 let payload = TelemetryPing.getPayload();
michael@0 69 let ping = { slug: "test-ping-" + gCreatedPings, reason: "test", payload: payload };
michael@0 70
michael@0 71 yield TelemetryFile.savePing(ping);
michael@0 72
michael@0 73 if (aAge) {
michael@0 74 // savePing writes to the file synchronously, so we're good to
michael@0 75 // modify the lastModifedTime now.
michael@0 76 let file = getSavePathForPing(ping);
michael@0 77 yield File.setDates(file, null, age);
michael@0 78 }
michael@0 79 gCreatedPings++;
michael@0 80 pings.push(ping);
michael@0 81 }
michael@0 82 return pings;
michael@0 83 });
michael@0 84 }
michael@0 85
michael@0 86 /**
michael@0 87 * Deletes locally saved pings in aPings if they
michael@0 88 * exist.
michael@0 89 *
michael@0 90 * @param aPings an Array of pings to delete.
michael@0 91 * @returns Promise
michael@0 92 */
michael@0 93 function clearPings(aPings) {
michael@0 94 return Task.spawn(function*() {
michael@0 95 for (let ping of aPings) {
michael@0 96 let path = getSavePathForPing(ping);
michael@0 97 yield File.remove(path);
michael@0 98 }
michael@0 99 });
michael@0 100 }
michael@0 101
michael@0 102 /**
michael@0 103 * Returns a handle for the file that aPing should be
michael@0 104 * stored in locally.
michael@0 105 *
michael@0 106 * @returns path
michael@0 107 */
michael@0 108 function getSavePathForPing(aPing) {
michael@0 109 return Path.join(Constants.Path.profileDir, PING_SAVE_FOLDER, aPing.slug);
michael@0 110 }
michael@0 111
michael@0 112 /**
michael@0 113 * Check if the number of TelemetryPings received by the
michael@0 114 * HttpServer is not equal to aExpectedNum.
michael@0 115 *
michael@0 116 * @param aExpectedNum the number of pings we expect to receive.
michael@0 117 */
michael@0 118 function assertReceivedPings(aExpectedNum) {
michael@0 119 do_check_eq(gSeenPings, aExpectedNum);
michael@0 120 }
michael@0 121
michael@0 122 /**
michael@0 123 * Throws if any pings in aPings is saved locally.
michael@0 124 *
michael@0 125 * @param aPings an Array of pings to check.
michael@0 126 * @returns Promise
michael@0 127 */
michael@0 128 function assertNotSaved(aPings) {
michael@0 129 return Task.spawn(function*() {
michael@0 130 let saved = 0;
michael@0 131 for (let ping of aPings) {
michael@0 132 let file = getSavePathForPing(ping);
michael@0 133 if (yield File.exists()) {
michael@0 134 saved++;
michael@0 135 }
michael@0 136 }
michael@0 137 if (saved > 0) {
michael@0 138 do_throw("Found " + saved + " unexpected saved pings.");
michael@0 139 }
michael@0 140 });
michael@0 141 }
michael@0 142
michael@0 143 /**
michael@0 144 * Our handler function for the HttpServer that simply
michael@0 145 * increments the gSeenPings global when it successfully
michael@0 146 * receives and decodes a TelemetryPing payload.
michael@0 147 *
michael@0 148 * @param aRequest the HTTP request sent from HttpServer.
michael@0 149 */
michael@0 150 function pingHandler(aRequest) {
michael@0 151 gSeenPings++;
michael@0 152 }
michael@0 153
michael@0 154 /**
michael@0 155 * Returns a Promise that resolves when gHttpServer has been
michael@0 156 * successfully shut down.
michael@0 157 *
michael@0 158 * @returns Promise
michael@0 159 */
michael@0 160 function stopHttpServer() {
michael@0 161 let deferred = Promise.defer();
michael@0 162 gHttpServer.stop(function() {
michael@0 163 deferred.resolve();
michael@0 164 })
michael@0 165 return deferred.promise;
michael@0 166 }
michael@0 167
michael@0 168 /**
michael@0 169 * Teardown a TelemetryPing instance and clear out any pending
michael@0 170 * pings to put as back in the starting state.
michael@0 171 */
michael@0 172 function resetTelemetry() {
michael@0 173 TelemetryPing.uninstall();
michael@0 174 // Quick and dirty way to clear TelemetryFile's pendingPings
michael@0 175 // collection, and put it back in its initial state.
michael@0 176 let gen = TelemetryFile.popPendingPings();
michael@0 177 for (let item of gen) {};
michael@0 178 }
michael@0 179
michael@0 180 /**
michael@0 181 * Creates and returns a TelemetryPing instance in "testing"
michael@0 182 * mode.
michael@0 183 */
michael@0 184 function startTelemetry() {
michael@0 185 return TelemetryPing.setup();
michael@0 186 }
michael@0 187
michael@0 188 function run_test() {
michael@0 189 gHttpServer.registerPrefixHandler("/submit/telemetry/", pingHandler);
michael@0 190 gHttpServer.start(-1);
michael@0 191 do_get_profile();
michael@0 192 Services.prefs.setBoolPref(TelemetryPing.Constants.PREF_ENABLED, true);
michael@0 193 Services.prefs.setCharPref(TelemetryPing.Constants.PREF_SERVER,
michael@0 194 "http://localhost:" + gHttpServer.identity.primaryPort);
michael@0 195 run_next_test();
michael@0 196 }
michael@0 197
michael@0 198 /**
michael@0 199 * Test that pings that are considered too old are just chucked out
michael@0 200 * immediately and never sent.
michael@0 201 */
michael@0 202 add_task(function test_expired_pings_are_deleted() {
michael@0 203 let expiredPings = yield createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
michael@0 204 yield startTelemetry();
michael@0 205 assertReceivedPings(0);
michael@0 206 yield assertNotSaved(expiredPings);
michael@0 207 yield resetTelemetry();
michael@0 208 });
michael@0 209
michael@0 210 /**
michael@0 211 * Test that really recent pings are not sent on Telemetry initialization.
michael@0 212 */
michael@0 213 add_task(function test_recent_pings_not_sent() {
michael@0 214 let recentPings = yield createSavedPings(RECENT_PINGS);
michael@0 215 yield startTelemetry();
michael@0 216 assertReceivedPings(0);
michael@0 217 yield resetTelemetry();
michael@0 218 yield clearPings(recentPings);
michael@0 219 });
michael@0 220
michael@0 221 /**
michael@0 222 * Create some recent, expired and overdue pings. The overdue pings should
michael@0 223 * trigger a send of all recent and overdue pings, but the expired pings
michael@0 224 * should just be deleted.
michael@0 225 */
michael@0 226 add_task(function test_overdue_pings_trigger_send() {
michael@0 227 let recentPings = yield createSavedPings(RECENT_PINGS);
michael@0 228 let expiredPings = yield createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
michael@0 229 let overduePings = yield createSavedPings(OVERDUE_PINGS, OVERDUE_PING_FILE_AGE);
michael@0 230
michael@0 231 yield startTelemetry();
michael@0 232 assertReceivedPings(TOTAL_EXPECTED_PINGS);
michael@0 233
michael@0 234 yield assertNotSaved(recentPings);
michael@0 235 yield assertNotSaved(expiredPings);
michael@0 236 yield assertNotSaved(overduePings);
michael@0 237 yield resetTelemetry();
michael@0 238 });
michael@0 239
michael@0 240 add_task(function teardown() {
michael@0 241 yield stopHttpServer();
michael@0 242 });

mercurial