| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 | #!/usr/bin/env node/* Copyright 2016 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *     http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* eslint-disable no-var */"use strict";var assert = require("assert");var fs = require("fs");var vm = require("vm");var SRC_DIR = __dirname + "/../../";var telemetryJsPath = "extensions/chromium/telemetry.js";var telemetryJsSource = fs.readFileSync(SRC_DIR + telemetryJsPath);var telemetryScript = new vm.Script(telemetryJsSource, {  filename: telemetryJsPath,  displayErrors: true,});var LOG_URL = /LOG_URL = '([^']+)'/.exec(telemetryJsSource)[1];// Create a minimal extension global that mocks the extension environment that// is used by telemetry.jsfunction createExtensionGlobal() {  var window = {};  // Whenever a "request" was made, the extra headers are appended to this list.  var test_requests = (window.test_requests = []);  // Extension API mocks.  window.window = window;  window.chrome = {};  window.chrome.extension = {};  window.chrome.extension.inIncognitoContext = false;  window.chrome.runtime = {};  window.chrome.runtime.id = "oemmndcbldboiebfnladdacbdfmadadm";  window.chrome.runtime.getManifest = function () {    return { version: "1.0.0" };  };  function createStorageAPI() {    var storageArea = {};    storageArea.get = function (key, callback) {      assert.equal(key, "disableTelemetry");      // chrome.storage.*. is async, but we make it synchronous to ease testing.      callback(storageArea.mock_data);    };    return storageArea;  }  window.chrome.storage = {};  window.chrome.storage.managed = createStorageAPI();  window.chrome.storage.local = createStorageAPI();  window.chrome.storage.sync = createStorageAPI();  // Other DOM.  window.navigator = {};  window.navigator.onLine = true;  window.navigator.userAgent =    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " +    "Chrome/50.0.2661.94 Safari/537.36";  window.localStorage = {};  var getRandomValues_state = 0;  window.crypto = {};  window.crypto.getRandomValues = function (buf) {    var state = getRandomValues_state++;    for (var i = 0; i < buf.length; ++i) {      // Totally random byte ;)      buf[i] = 0x42 + state;    }    return buf;  };  // Network-related mocks.  window.Request = {};  window.Request.prototype = {    get mode() {      throw new TypeError("Illegal invocation");    },  };  window.fetch = function (url, options) {    assert.equal(url, LOG_URL);    assert.equal(options.method, "POST");    assert.equal(options.mode, "cors");    assert.ok(!options.body);    test_requests.push(options.headers);  };  window.Headers = function (headers) {    headers = JSON.parse(JSON.stringify(headers)); // Clone.    Object.keys(headers).forEach(function (k) {      headers[k] = String(headers[k]);    });    return headers;  };  window.XMLHttpRequest = function () {    var invoked = {      open: false,      send: false,    };    var headers = {};    return {      open(method, url) {        assert.equal(invoked.open, false);        invoked.open = true;        assert.equal(method, "POST");        assert.equal(url, LOG_URL);      },      setRequestHeader(k, v) {        assert.equal(invoked.open, true);        headers[k] = String(v);      },      send(body) {        assert.equal(invoked.open, true);        assert.equal(invoked.send, false);        invoked.send = true;        assert.ok(!body);        test_requests.push(headers);      },    };  };  // Time-related logic.  var timers = [];  window.setInterval = function (callback, ms) {    assert.equal(typeof callback, "function");    timers.push(callback);  };  window.Date = {    test_now_value: Date.now(),    now() {      return window.Date.test_now_value;    },  };  window.test_fireTimers = function () {    assert.ok(timers.length);    timers.forEach(function (timer) {      timer();    });  };  return window;}// Simulate an update of the browser.function updateBrowser(window) {  window.navigator.userAgent = window.navigator.userAgent.replace(    /Chrome\/(\d+)/,    function (_, v) {      return "Chrome/" + (parseInt(v) + 1);    }  );}var tests = [  function test_first_run() {    // Default settings, run extension for the first time.    var window = createExtensionGlobal();    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);  },  function test_first_run_incognito() {    // The extension should not send any requests when in incognito mode.    var window = createExtensionGlobal();    window.chrome.extension.inIncognitoContext = true;    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, []);  },  function test_storage_managed_unavailable() {    var window = createExtensionGlobal();    delete window.chrome.storage.managed;    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);  },  function test_managed_pref() {    var window = createExtensionGlobal();    window.chrome.storage.managed.mock_data = {      disableTelemetry: true,    };    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, []);  },  function test_local_pref() {    var window = createExtensionGlobal();    window.chrome.storage.local.mock_data = {      disableTelemetry: true,    };    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, []);  },  function test_managed_pref_is_overridden() {    var window = createExtensionGlobal();    window.chrome.storage.managed.mock_data = {      disableTelemetry: true,    };    window.chrome.storage.sync.mock_data = {      disableTelemetry: false,    };    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);  },  function test_run_extension_again() {    var window = createExtensionGlobal();    telemetryScript.runInNewContext(window);    telemetryScript.runInNewContext(window);    // Only one request should be sent because of rate-limiting.    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);    // Simulate that quite some hours passed, but it's still rate-limited.    window.Date.test_now_value += 11 * 36e5;    telemetryScript.runInNewContext(window);    // Only one request should be sent because of rate-limiting.    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);    // Another hour passes and the request should not be rate-limited any more.    window.Date.test_now_value += 1 * 36e5;    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);  },  function test_running_for_a_while() {    var window = createExtensionGlobal();    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);    // Simulate that the timer fired 11 hours since the last ping. The request    // should still be rate-limited.    window.Date.test_now_value += 11 * 36e5;    window.test_fireTimers();    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);    // Another hour passes and the request should not be rate-limited any more.    window.Date.test_now_value += 1 * 36e5;    window.test_fireTimers();    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);  },  function test_browser_update() {    var window = createExtensionGlobal();    telemetryScript.runInNewContext(window);    updateBrowser(window);    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },      {        // Generate a new ID for better privacy.        "Deduplication-Id": "4343434343",        "Extension-Version": "1.0.0",      },    ]);  },  function test_browser_update_between_pref_toggle() {    var window = createExtensionGlobal();    telemetryScript.runInNewContext(window);    window.chrome.storage.local.mock_data = {      disableTelemetry: true,    };    updateBrowser(window);    telemetryScript.runInNewContext(window);    window.chrome.storage.local.mock_data = {      disableTelemetry: false,    };    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },      {        // Generate a new ID for better privacy, even if the update happened        // while telemetry was disabled.        "Deduplication-Id": "4343434343",        "Extension-Version": "1.0.0",      },    ]);  },  function test_extension_update() {    var window = createExtensionGlobal();    telemetryScript.runInNewContext(window);    window.chrome.runtime.getManifest = function () {      return { version: "1.0.1" };    };    window.Date.test_now_value += 12 * 36e5;    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },      {        // The ID did not change because the browser version did not change.        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.1",      },    ]);  },  function test_unofficial_build() {    var window = createExtensionGlobal();    var didWarn = false;    window.console = {};    window.console.warn = function () {      didWarn = true;    };    window.chrome.runtime.id = "abcdefghijklmnopabcdefghijklmnop";    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, []);    assert.ok(didWarn);  },  function test_fetch_is_supported() {    var window = createExtensionGlobal();    // XMLHttpRequest should not be called when fetch is available. So removing    // the XMLHttpRequest API should not change behavior.    delete window.XMLHttpRequest;    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);  },  function test_fetch_not_supported() {    var window = createExtensionGlobal();    delete window.fetch;    delete window.Request;    delete window.Headers;    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);  },  function test_fetch_mode_not_supported() {    var window = createExtensionGlobal();    delete window.Request.prototype.mode;    window.fetch = function () {      throw new Error("Unexpected call to fetch!");    };    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);  },  function test_network_offline() {    var window = createExtensionGlobal();    // Simulate that the network is down for sure.    window.navigator.onLine = false;    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, []);    // Simulate that the network might be up.    window.navigator.onLine = true;    telemetryScript.runInNewContext(window);    assert.deepEqual(window.test_requests, [      {        "Deduplication-Id": "4242424242",        "Extension-Version": "1.0.0",      },    ]);  },];var test_i = 0;(function next() {  var test = tests[test_i++];  if (!test) {    console.log("All tests completed.");    return;  }  console.log("Running test " + test_i + "/" + tests.length + ": " + test.name);  test();  process.nextTick(next);})();
 |