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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,242 @@
     1.4 +/* Any copyright is dedicated to the Public Domain.
     1.5 +   http://creativecommons.org/publicdomain/zero/1.0/
     1.6 +
     1.7 +/**
     1.8 + * This test case populates the profile with some fake stored
     1.9 + * pings, and checks that:
    1.10 + *
    1.11 + * 1) Pings that are considered "expired" are deleted and never sent.
    1.12 + * 2) Pings that are considered "overdue" trigger a send of all
    1.13 + *    overdue and recent pings.
    1.14 + */
    1.15 +
    1.16 +"use strict"
    1.17 +
    1.18 +const Cc = Components.classes;
    1.19 +const Ci = Components.interfaces;
    1.20 +const Cr = Components.results;
    1.21 +const Cu = Components.utils;
    1.22 +
    1.23 +Cu.import("resource://gre/modules/Services.jsm", this);
    1.24 +Cu.import("resource://testing-common/httpd.js", this);
    1.25 +Cu.import("resource://gre/modules/Promise.jsm", this);
    1.26 +Cu.import("resource://gre/modules/TelemetryFile.jsm", this);
    1.27 +Cu.import("resource://gre/modules/TelemetryPing.jsm", this);
    1.28 +Cu.import("resource://gre/modules/Task.jsm", this);
    1.29 +let {OS: {File, Path, Constants}} = Cu.import("resource://gre/modules/osfile.jsm", {});
    1.30 +
    1.31 +// We increment TelemetryFile's MAX_PING_FILE_AGE and
    1.32 +// OVERDUE_PING_FILE_AGE by 1 minute so that our test pings exceed
    1.33 +// those points in time, even taking into account file system imprecision.
    1.34 +const ONE_MINUTE_MS = 60 * 1000;
    1.35 +const EXPIRED_PING_FILE_AGE = TelemetryFile.MAX_PING_FILE_AGE + ONE_MINUTE_MS;
    1.36 +const OVERDUE_PING_FILE_AGE = TelemetryFile.OVERDUE_PING_FILE_AGE + ONE_MINUTE_MS;
    1.37 +
    1.38 +const PING_SAVE_FOLDER = "saved-telemetry-pings";
    1.39 +const PING_TIMEOUT_LENGTH = 5000;
    1.40 +const EXPIRED_PINGS = 5;
    1.41 +const OVERDUE_PINGS = 6;
    1.42 +const RECENT_PINGS = 4;
    1.43 +
    1.44 +const TOTAL_EXPECTED_PINGS = OVERDUE_PINGS + RECENT_PINGS;
    1.45 +
    1.46 +let gHttpServer = new HttpServer();
    1.47 +let gCreatedPings = 0;
    1.48 +let gSeenPings = 0;
    1.49 +
    1.50 +/**
    1.51 + * Creates some TelemetryPings for the current session and
    1.52 + * saves them to disk. Each ping gets a unique ID slug based on
    1.53 + * an incrementor.
    1.54 + *
    1.55 + * @param aNum the number of pings to create.
    1.56 + * @param aAge the age in milliseconds to offset from now. A value
    1.57 + *             of 10 would make the ping 10ms older than now, for
    1.58 + *             example.
    1.59 + * @returns Promise
    1.60 + * @resolve an Array with the created pings.
    1.61 + */
    1.62 +function createSavedPings(aNum, aAge) {
    1.63 +  return Task.spawn(function*(){
    1.64 +    // Create a TelemetryPing service that we can generate payloads from.
    1.65 +    // Luckily, the TelemetryPing constructor does nothing that we need to
    1.66 +    // clean up.
    1.67 +    let pings = [];
    1.68 +    let age = Date.now() - aAge;
    1.69 +
    1.70 +    for (let i = 0; i < aNum; ++i) {
    1.71 +      let payload = TelemetryPing.getPayload();
    1.72 +      let ping = { slug: "test-ping-" + gCreatedPings, reason: "test", payload: payload };
    1.73 +
    1.74 +      yield TelemetryFile.savePing(ping);
    1.75 +
    1.76 +      if (aAge) {
    1.77 +        // savePing writes to the file synchronously, so we're good to
    1.78 +        // modify the lastModifedTime now.
    1.79 +        let file = getSavePathForPing(ping);
    1.80 +        yield File.setDates(file, null, age);
    1.81 +      }
    1.82 +      gCreatedPings++;
    1.83 +      pings.push(ping);
    1.84 +    }
    1.85 +    return pings;
    1.86 +  });
    1.87 +}
    1.88 +
    1.89 +/**
    1.90 + * Deletes locally saved pings in aPings if they
    1.91 + * exist.
    1.92 + *
    1.93 + * @param aPings an Array of pings to delete.
    1.94 + * @returns Promise
    1.95 + */
    1.96 +function clearPings(aPings) {
    1.97 +  return Task.spawn(function*() {
    1.98 +    for (let ping of aPings) {
    1.99 +      let path = getSavePathForPing(ping);
   1.100 +      yield File.remove(path);
   1.101 +    }
   1.102 +  });
   1.103 +}
   1.104 +
   1.105 +/**
   1.106 + * Returns a handle for the file that aPing should be
   1.107 + * stored in locally.
   1.108 + *
   1.109 + * @returns path
   1.110 + */
   1.111 +function getSavePathForPing(aPing) {
   1.112 +  return Path.join(Constants.Path.profileDir, PING_SAVE_FOLDER, aPing.slug);
   1.113 +}
   1.114 +
   1.115 +/**
   1.116 + * Check if the number of TelemetryPings received by the
   1.117 + * HttpServer is not equal to aExpectedNum.
   1.118 + *
   1.119 + * @param aExpectedNum the number of pings we expect to receive.
   1.120 + */
   1.121 +function assertReceivedPings(aExpectedNum) {
   1.122 +  do_check_eq(gSeenPings, aExpectedNum);
   1.123 +}
   1.124 +
   1.125 +/**
   1.126 + * Throws if any pings in aPings is saved locally.
   1.127 + *
   1.128 + * @param aPings an Array of pings to check.
   1.129 + * @returns Promise
   1.130 + */
   1.131 +function assertNotSaved(aPings) {
   1.132 +  return Task.spawn(function*() {
   1.133 +    let saved = 0;
   1.134 +    for (let ping of aPings) {
   1.135 +      let file = getSavePathForPing(ping);
   1.136 +      if (yield File.exists()) {
   1.137 +        saved++;
   1.138 +      }
   1.139 +    }
   1.140 +    if (saved > 0) {
   1.141 +      do_throw("Found " + saved + " unexpected saved pings.");
   1.142 +    }
   1.143 +  });
   1.144 +}
   1.145 +
   1.146 +/**
   1.147 + * Our handler function for the HttpServer that simply
   1.148 + * increments the gSeenPings global when it successfully
   1.149 + * receives and decodes a TelemetryPing payload.
   1.150 + *
   1.151 + * @param aRequest the HTTP request sent from HttpServer.
   1.152 + */
   1.153 +function pingHandler(aRequest) {
   1.154 +  gSeenPings++;
   1.155 +}
   1.156 +
   1.157 +/**
   1.158 + * Returns a Promise that resolves when gHttpServer has been
   1.159 + * successfully shut down.
   1.160 + *
   1.161 + * @returns Promise
   1.162 + */
   1.163 +function stopHttpServer() {
   1.164 +  let deferred = Promise.defer();
   1.165 +  gHttpServer.stop(function() {
   1.166 +    deferred.resolve();
   1.167 +  })
   1.168 +  return deferred.promise;
   1.169 +}
   1.170 +
   1.171 +/**
   1.172 + * Teardown a TelemetryPing instance and clear out any pending
   1.173 + * pings to put as back in the starting state.
   1.174 + */
   1.175 +function resetTelemetry() {
   1.176 +  TelemetryPing.uninstall();
   1.177 +  // Quick and dirty way to clear TelemetryFile's pendingPings
   1.178 +  // collection, and put it back in its initial state.
   1.179 +  let gen = TelemetryFile.popPendingPings();
   1.180 +  for (let item of gen) {};
   1.181 +}
   1.182 +
   1.183 +/**
   1.184 + * Creates and returns a TelemetryPing instance in "testing"
   1.185 + * mode.
   1.186 + */
   1.187 +function startTelemetry() {
   1.188 +  return TelemetryPing.setup();
   1.189 +}
   1.190 +
   1.191 +function run_test() {
   1.192 +  gHttpServer.registerPrefixHandler("/submit/telemetry/", pingHandler);
   1.193 +  gHttpServer.start(-1);
   1.194 +  do_get_profile();
   1.195 +  Services.prefs.setBoolPref(TelemetryPing.Constants.PREF_ENABLED, true);
   1.196 +  Services.prefs.setCharPref(TelemetryPing.Constants.PREF_SERVER,
   1.197 +                             "http://localhost:" + gHttpServer.identity.primaryPort);
   1.198 +  run_next_test();
   1.199 +}
   1.200 +
   1.201 +/**
   1.202 + * Test that pings that are considered too old are just chucked out
   1.203 + * immediately and never sent.
   1.204 + */
   1.205 +add_task(function test_expired_pings_are_deleted() {
   1.206 +  let expiredPings = yield createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
   1.207 +  yield startTelemetry();
   1.208 +  assertReceivedPings(0);
   1.209 +  yield assertNotSaved(expiredPings);
   1.210 +  yield resetTelemetry();
   1.211 +});
   1.212 +
   1.213 +/**
   1.214 + * Test that really recent pings are not sent on Telemetry initialization.
   1.215 + */
   1.216 +add_task(function test_recent_pings_not_sent() {
   1.217 +  let recentPings = yield createSavedPings(RECENT_PINGS);
   1.218 +  yield startTelemetry();
   1.219 +  assertReceivedPings(0);
   1.220 +  yield resetTelemetry();
   1.221 +  yield clearPings(recentPings);
   1.222 +});
   1.223 +
   1.224 +/**
   1.225 + * Create some recent, expired and overdue pings. The overdue pings should
   1.226 + * trigger a send of all recent and overdue pings, but the expired pings
   1.227 + * should just be deleted.
   1.228 + */
   1.229 +add_task(function test_overdue_pings_trigger_send() {
   1.230 +  let recentPings = yield createSavedPings(RECENT_PINGS);
   1.231 +  let expiredPings = yield createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
   1.232 +  let overduePings = yield createSavedPings(OVERDUE_PINGS, OVERDUE_PING_FILE_AGE);
   1.233 +
   1.234 +  yield startTelemetry();
   1.235 +  assertReceivedPings(TOTAL_EXPECTED_PINGS);
   1.236 +
   1.237 +  yield assertNotSaved(recentPings);
   1.238 +  yield assertNotSaved(expiredPings);
   1.239 +  yield assertNotSaved(overduePings);
   1.240 +  yield resetTelemetry();
   1.241 +});
   1.242 +
   1.243 +add_task(function teardown() {
   1.244 +  yield stopHttpServer();
   1.245 +});

mercurial