mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 15:55:36 +00:00
Bug 1583897 - Send a telemetry event for new sendtab. r=tcsc,eoger,lina
Differential Revision: https://phabricator.services.mozilla.com/D48153 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
0b08adcfbb
commit
0481284ac7
@ -92,6 +92,12 @@ ChromeUtils.defineModuleGetter(
|
||||
"resource://gre/modules/FxAccountsProfile.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"FxAccountsTelemetry",
|
||||
"resource://gre/modules/FxAccountsTelemetry.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
Preferences: "resource://gre/modules/Preferences.jsm",
|
||||
});
|
||||
@ -409,6 +415,10 @@ class FxAccounts {
|
||||
return this._internal.keys;
|
||||
}
|
||||
|
||||
get telemetry() {
|
||||
return this._internal.telemetry;
|
||||
}
|
||||
|
||||
_withCurrentAccountState(func) {
|
||||
return this._internal.withCurrentAccountState(func);
|
||||
}
|
||||
@ -858,6 +868,14 @@ FxAccountsInternal.prototype = {
|
||||
return this._device;
|
||||
},
|
||||
|
||||
_telemetry: null,
|
||||
get telemetry() {
|
||||
if (!this._telemetry) {
|
||||
this._telemetry = new FxAccountsTelemetry();
|
||||
}
|
||||
return this._telemetry;
|
||||
},
|
||||
|
||||
// A hook-point for tests who may want a mocked AccountState or mocked storage.
|
||||
newAccountState(credentials) {
|
||||
let storage = new FxAccountsStorageManager();
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
const EXPORTED_SYMBOLS = ["SendTab", "FxAccountsCommands"];
|
||||
|
||||
const { COMMAND_SENDTAB, log } = ChromeUtils.import(
|
||||
const { COMMAND_SENDTAB, COMMAND_SENDTAB_TAIL, log } = ChromeUtils.import(
|
||||
"resource://gre/modules/FxAccountsCommon.js"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
@ -149,7 +149,7 @@ class FxAccountsCommands {
|
||||
switch (command) {
|
||||
case COMMAND_SENDTAB:
|
||||
try {
|
||||
const { title, uri } = await this.sendTab.handle(payload);
|
||||
const { title, uri } = await this.sendTab.handle(senderId, payload);
|
||||
log.info(
|
||||
`Tab received with FxA commands: ${title} from ${
|
||||
sender ? sender.name : "Unknown device"
|
||||
@ -192,10 +192,9 @@ class SendTab {
|
||||
*/
|
||||
async send(to, tab) {
|
||||
log.info(`Sending a tab to ${to.length} devices.`);
|
||||
const flowID = this._fxai.telemetry.generateFlowID();
|
||||
const encoder = new TextEncoder("utf8");
|
||||
const data = {
|
||||
entries: [{ title: tab.title, url: tab.url }],
|
||||
};
|
||||
const data = { entries: [{ title: tab.title, url: tab.url }] };
|
||||
const bytes = encoder.encode(JSON.stringify(data));
|
||||
const report = {
|
||||
succeeded: [],
|
||||
@ -204,8 +203,14 @@ class SendTab {
|
||||
for (let device of to) {
|
||||
try {
|
||||
const encrypted = await this._encrypt(bytes, device);
|
||||
const payload = { encrypted };
|
||||
const payload = { encrypted, flowID };
|
||||
await this._commands.invoke(COMMAND_SENDTAB, device, payload); // FxA needs an object.
|
||||
this._fxai.telemetry.recordEvent(
|
||||
"command-sent",
|
||||
COMMAND_SENDTAB_TAIL,
|
||||
this._fxai.telemetry.sanitizeDeviceId(device.id),
|
||||
{ flowID }
|
||||
);
|
||||
report.succeeded.push(device);
|
||||
} catch (error) {
|
||||
log.error("Error while invoking a send tab command.", error);
|
||||
@ -228,7 +233,7 @@ class SendTab {
|
||||
}
|
||||
|
||||
// Handle incoming send tab payload, called by FxAccountsCommands.
|
||||
async handle({ encrypted }) {
|
||||
async handle(senderID, { encrypted, flowID }) {
|
||||
const bytes = await this._decrypt(encrypted);
|
||||
const decoder = new TextDecoder("utf8");
|
||||
const data = JSON.parse(decoder.decode(bytes));
|
||||
@ -236,6 +241,13 @@ class SendTab {
|
||||
? data.current
|
||||
: data.entries.length - 1;
|
||||
const { title, url: uri } = data.entries[current];
|
||||
this._fxai.telemetry.recordEvent(
|
||||
"command-received",
|
||||
COMMAND_SENDTAB_TAIL,
|
||||
this._fxai.telemetry.sanitizeDeviceId(senderID),
|
||||
{ flowID }
|
||||
);
|
||||
|
||||
return {
|
||||
title,
|
||||
uri,
|
||||
|
@ -78,7 +78,12 @@ exports.ON_PROFILE_CHANGE_NOTIFICATION = "fxaccounts:profilechange"; // WebChann
|
||||
exports.ON_ACCOUNT_STATE_CHANGE_NOTIFICATION = "fxaccounts:statechange";
|
||||
exports.ON_NEW_DEVICE_ID = "fxaccounts:new_device_id";
|
||||
|
||||
exports.COMMAND_SENDTAB = "https://identity.mozilla.com/cmd/open-uri";
|
||||
// The common prefix for all commands.
|
||||
exports.COMMAND_PREFIX = "https://identity.mozilla.com/cmd/";
|
||||
|
||||
// The commands we support - only the _TAIL values are recorded in telemetry.
|
||||
exports.COMMAND_SENDTAB_TAIL = "open-uri";
|
||||
exports.COMMAND_SENDTAB = exports.COMMAND_PREFIX + exports.COMMAND_SENDTAB_TAIL;
|
||||
|
||||
// OAuth
|
||||
exports.FX_OAUTH_CLIENT_ID = "5882386c6d801776";
|
||||
|
55
services/fxaccounts/FxAccountsTelemetry.jsm
Normal file
55
services/fxaccounts/FxAccountsTelemetry.jsm
Normal file
@ -0,0 +1,55 @@
|
||||
/* 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";
|
||||
|
||||
// FxA Telemetry support. For hysterical raisins, the actual implementation
|
||||
// is inside "sync". We should move the core implementation somewhere that's
|
||||
// sanely shared (eg, services-common?), but let's wait and see where we end up
|
||||
// first...
|
||||
|
||||
// We use this observers module because we leverage its support for richer
|
||||
// "subject" data.
|
||||
const { Observers } = ChromeUtils.import(
|
||||
"resource://services-common/observers.js"
|
||||
);
|
||||
|
||||
class FxAccountsTelemetry {
|
||||
recordEvent(object, method, value, extra = undefined) {
|
||||
// We need to ensure the telemetry module is loaded.
|
||||
ChromeUtils.import("resource://services-sync/telemetry.js");
|
||||
// Now it will be listening for the notifications...
|
||||
Observers.notify("fxa:telemetry:event", { object, method, value, extra });
|
||||
}
|
||||
|
||||
// A flow ID can be anything that's "probably" unique, so for now use a UUID.
|
||||
generateFlowID() {
|
||||
return Cc["@mozilla.org/uuid-generator;1"]
|
||||
.getService(Ci.nsIUUIDGenerator)
|
||||
.generateUUID()
|
||||
.toString()
|
||||
.slice(1, -1);
|
||||
}
|
||||
|
||||
// Sanitize the ID of a device into something suitable for including in the
|
||||
// ping. Returns null if no transformation is possible.
|
||||
sanitizeDeviceId(deviceId) {
|
||||
// We only know how to hash it for sync users, which kinda sucks.
|
||||
let xps =
|
||||
this._weaveXPCOM ||
|
||||
Cc["@mozilla.org/weave/service;1"].getService(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
if (!xps.enabled) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return xps.Weave.Service.identity.hashedDeviceID(deviceId);
|
||||
} catch {
|
||||
// sadly this can happen in various scenarios, so don't complain.
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = ["FxAccountsTelemetry"];
|
@ -30,6 +30,7 @@ EXTRA_JS_MODULES += [
|
||||
'FxAccountsProfileClient.jsm',
|
||||
'FxAccountsPush.jsm',
|
||||
'FxAccountsStorage.jsm',
|
||||
'FxAccountsTelemetry.jsm',
|
||||
'FxAccountsWebChannel.jsm',
|
||||
]
|
||||
|
||||
|
@ -7,6 +7,36 @@ const { FxAccountsCommands, SendTab } = ChromeUtils.import(
|
||||
"resource://gre/modules/FxAccountsCommands.js"
|
||||
);
|
||||
|
||||
const { COMMAND_SENDTAB, COMMAND_SENDTAB_TAIL } = ChromeUtils.import(
|
||||
"resource://gre/modules/FxAccountsCommon.js"
|
||||
);
|
||||
|
||||
class TelemetryMock {
|
||||
constructor() {
|
||||
this._events = [];
|
||||
this._uuid_counter = 0;
|
||||
}
|
||||
|
||||
recordEvent(object, method, value, extra = undefined) {
|
||||
this._events.push({ object, method, value, extra });
|
||||
}
|
||||
|
||||
generateFlowID() {
|
||||
this._uuid_counter += 1;
|
||||
return this._uuid_counter.toString();
|
||||
}
|
||||
|
||||
sanitizeDeviceId(id) {
|
||||
return id + "-san";
|
||||
}
|
||||
}
|
||||
|
||||
function FxaInternalMock() {
|
||||
return {
|
||||
telemetry: new TelemetryMock(),
|
||||
};
|
||||
}
|
||||
|
||||
add_task(async function test_sendtab_isDeviceCompatible() {
|
||||
const sendTab = new SendTab(null, null);
|
||||
let device = { name: "My device" };
|
||||
@ -31,14 +61,19 @@ add_task(async function test_sendtab_send() {
|
||||
Assert.equal(payload.encrypted, "encryptedpayload");
|
||||
}),
|
||||
};
|
||||
const sendTab = new SendTab(commands, null);
|
||||
const fxai = FxaInternalMock();
|
||||
const sendTab = new SendTab(commands, fxai);
|
||||
sendTab._encrypt = (bytes, device) => {
|
||||
if (device.name == "Device 2") {
|
||||
throw new Error("Encrypt error!");
|
||||
}
|
||||
return "encryptedpayload";
|
||||
};
|
||||
const to = [{ name: "Device 1" }, { name: "Device 2" }, { name: "Device 3" }];
|
||||
const to = [
|
||||
{ name: "Device 1" },
|
||||
{ name: "Device 2" },
|
||||
{ id: "dev3", name: "Device 3" },
|
||||
];
|
||||
const tab = { title: "Foo", url: "https://foo.bar/" };
|
||||
const report = await sendTab.send(to, tab);
|
||||
Assert.equal(report.succeeded.length, 1);
|
||||
@ -49,6 +84,62 @@ add_task(async function test_sendtab_send() {
|
||||
Assert.equal(report.failed[1].device.name, "Device 2");
|
||||
Assert.equal(report.failed[1].error.message, "Encrypt error!");
|
||||
Assert.ok(commands.invoke.calledTwice);
|
||||
Assert.deepEqual(fxai.telemetry._events, [
|
||||
{
|
||||
object: "command-sent",
|
||||
method: COMMAND_SENDTAB_TAIL,
|
||||
value: "dev3-san",
|
||||
extra: { flowID: "1" },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
add_task(async function test_sendtab_receive() {
|
||||
// We are testing 'receive' here, but might as well go through 'send'
|
||||
// to package the data and for additional testing...
|
||||
const commands = {
|
||||
_invokes: [],
|
||||
invoke(cmd, device, payload) {
|
||||
this._invokes.push({ cmd, device, payload });
|
||||
},
|
||||
};
|
||||
|
||||
const fxai = FxaInternalMock();
|
||||
const sendTab = new SendTab(commands, fxai);
|
||||
sendTab._encrypt = (bytes, device) => {
|
||||
return bytes;
|
||||
};
|
||||
sendTab._decrypt = bytes => {
|
||||
return bytes;
|
||||
};
|
||||
const tab = { title: "tab title", url: "http://example.com" };
|
||||
const to = [{ id: "devid", name: "The Device" }];
|
||||
|
||||
await sendTab.send(to, tab);
|
||||
Assert.equal(commands._invokes.length, 1);
|
||||
|
||||
for (let { cmd, device, payload } of commands._invokes) {
|
||||
Assert.equal(cmd, COMMAND_SENDTAB);
|
||||
Assert.deepEqual(await sendTab.handle(device.id, payload), {
|
||||
title: "tab title",
|
||||
uri: "http://example.com",
|
||||
});
|
||||
}
|
||||
|
||||
Assert.deepEqual(fxai.telemetry._events, [
|
||||
{
|
||||
object: "command-sent",
|
||||
method: COMMAND_SENDTAB_TAIL,
|
||||
value: "devid-san",
|
||||
extra: { flowID: "1" },
|
||||
},
|
||||
{
|
||||
object: "command-received",
|
||||
method: COMMAND_SENDTAB_TAIL,
|
||||
value: "devid-san",
|
||||
extra: { flowID: "1" },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
add_task(async function test_commands_pollDeviceCommands_push() {
|
||||
|
@ -9,45 +9,27 @@ var EXPORTED_SYMBOLS = ["SyncTelemetry"];
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm");
|
||||
const { AuthenticationError } = ChromeUtils.import(
|
||||
"resource://services-sync/browserid_identity.js"
|
||||
);
|
||||
const { Weave } = ChromeUtils.import("resource://services-sync/main.js");
|
||||
const { Status } = ChromeUtils.import("resource://services-sync/status.js");
|
||||
const { Svc } = ChromeUtils.import("resource://services-sync/util.js");
|
||||
const { Resource } = ChromeUtils.import("resource://services-sync/resource.js");
|
||||
const { Observers } = ChromeUtils.import(
|
||||
"resource://services-common/observers.js"
|
||||
);
|
||||
const { Async } = ChromeUtils.import("resource://services-common/async.js");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
Async: "resource://services-common/async.js",
|
||||
AuthenticationError: "resource://services-sync/browserid_identity.js",
|
||||
Log: "resource://gre/modules/Log.jsm",
|
||||
ObjectUtils: "resource://gre/modules/ObjectUtils.jsm",
|
||||
Observers: "resource://services-common/observers.js",
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
Resource: "resource://services-sync/resource.js",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
Status: "resource://services-sync/status.js",
|
||||
Svc: "resource://services-sync/util.js",
|
||||
TelemetryController: "resource://gre/modules/TelemetryController.jsm",
|
||||
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
|
||||
TelemetryUtils: "resource://gre/modules/TelemetryUtils.jsm",
|
||||
Weave: "resource://services-sync/main.js",
|
||||
});
|
||||
|
||||
let constants = {};
|
||||
ChromeUtils.import("resource://services-sync/constants.js", constants);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"TelemetryController",
|
||||
"resource://gre/modules/TelemetryController.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"TelemetryUtils",
|
||||
"resource://gre/modules/TelemetryUtils.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"TelemetryEnvironment",
|
||||
"resource://gre/modules/TelemetryEnvironment.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"ObjectUtils",
|
||||
"resource://gre/modules/ObjectUtils.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"Telemetry",
|
||||
@ -74,6 +56,8 @@ const TOPICS = [
|
||||
|
||||
"weave:telemetry:event",
|
||||
"weave:telemetry:histogram",
|
||||
// and we are now used by FxA, so a custom event for that.
|
||||
"fxa:telemetry:event",
|
||||
];
|
||||
|
||||
const PING_FORMAT_VERSION = 1;
|
||||
@ -696,6 +680,20 @@ class SyncTelemetryImpl {
|
||||
return false;
|
||||
}
|
||||
|
||||
maybeSubmitForInterval() {
|
||||
// We want to submit the ping every `this.submissionInterval` but only when
|
||||
// there's no current sync in progress, otherwise we may end up submitting
|
||||
// the sync and the events caused by it in different pings.
|
||||
if (
|
||||
this.current == null &&
|
||||
Telemetry.msSinceProcessStart() - this.lastSubmissionTime >
|
||||
this.submissionInterval
|
||||
) {
|
||||
this.finish("schedule");
|
||||
this.lastSubmissionTime = Telemetry.msSinceProcessStart();
|
||||
}
|
||||
}
|
||||
|
||||
onSyncFinished(error) {
|
||||
if (!this.current) {
|
||||
log.warn("onSyncFinished but we aren't recording");
|
||||
@ -724,13 +722,7 @@ class SyncTelemetryImpl {
|
||||
++this.discarded;
|
||||
}
|
||||
this.current = null;
|
||||
if (
|
||||
Telemetry.msSinceProcessStart() - this.lastSubmissionTime >
|
||||
this.submissionInterval
|
||||
) {
|
||||
this.finish("schedule");
|
||||
this.lastSubmissionTime = Telemetry.msSinceProcessStart();
|
||||
}
|
||||
this.maybeSubmitForInterval();
|
||||
}
|
||||
|
||||
_addHistogram(hist) {
|
||||
@ -776,6 +768,7 @@ class SyncTelemetryImpl {
|
||||
event.push(extra);
|
||||
}
|
||||
this.events.push(event);
|
||||
this.maybeSubmitForInterval();
|
||||
}
|
||||
|
||||
observe(subject, topic, data) {
|
||||
@ -853,6 +846,7 @@ class SyncTelemetryImpl {
|
||||
break;
|
||||
|
||||
case "weave:telemetry:event":
|
||||
case "fxa:telemetry:event":
|
||||
this._recordEvent(subject);
|
||||
break;
|
||||
|
||||
|
@ -390,9 +390,40 @@ async function wait_for_ping(callback, allowErrorPings, getFullPing = false) {
|
||||
return record.syncs[0];
|
||||
}
|
||||
|
||||
// Short helper for wait_for_ping
|
||||
function sync_and_validate_telem(allowErrorPings, getFullPing = false) {
|
||||
return wait_for_ping(() => Service.sync(), allowErrorPings, getFullPing);
|
||||
// Perform a sync and validate all telemetry caused by the sync. If fnValidate
|
||||
// is null, we just check the ping records success. If fnValidate is specified,
|
||||
// then the sync must have recorded just a single sync, and that sync will be
|
||||
// passed to the function to be checked.
|
||||
async function sync_and_validate_telem(fnValidate = null) {
|
||||
let numErrors = 0;
|
||||
let telem = get_sync_test_telemetry();
|
||||
let oldSubmit = telem.submit;
|
||||
try {
|
||||
telem.submit = function(record) {
|
||||
// This is called via an observer, so failures here don't cause the test
|
||||
// to fail :(
|
||||
try {
|
||||
// All pings must be valid.
|
||||
assert_valid_ping(record);
|
||||
if (fnValidate) {
|
||||
// for historical reasons these callbacks expect a "sync" record, not
|
||||
// the entire ping.
|
||||
Assert.equal(record.syncs.length, 1);
|
||||
fnValidate(record.syncs[0]);
|
||||
} else {
|
||||
// no validation function means it must be a "success" ping.
|
||||
assert_success_ping(record);
|
||||
}
|
||||
} catch (ex) {
|
||||
print("Failure in ping validation callback", ex, "\n", ex.stack);
|
||||
numErrors += 1;
|
||||
}
|
||||
};
|
||||
await Service.sync();
|
||||
Assert.ok(numErrors == 0, "There were telemetry validation errors");
|
||||
} finally {
|
||||
telem.submit = oldSubmit;
|
||||
}
|
||||
}
|
||||
|
||||
// Used for the (many) cases where we do a 'partial' sync, where only a single
|
||||
|
@ -133,8 +133,12 @@ add_task(async function test_locally_changed_keys() {
|
||||
|
||||
_("HMAC error count: " + hmacErrorCount);
|
||||
// Now syncing should succeed, after one HMAC error.
|
||||
let ping = await wait_for_ping(() => Service.sync(), true);
|
||||
equal(ping.engines.find(e => e.name == "history").incoming.applied, 5);
|
||||
await sync_and_validate_telem(ping => {
|
||||
Assert.equal(
|
||||
ping.engines.find(e => e.name == "history").incoming.applied,
|
||||
5
|
||||
);
|
||||
});
|
||||
|
||||
Assert.equal(hmacErrorCount, 1);
|
||||
_(
|
||||
@ -189,12 +193,13 @@ add_task(async function test_locally_changed_keys() {
|
||||
Service.lastHMACEvent = 0;
|
||||
|
||||
_("Syncing...");
|
||||
ping = await sync_and_validate_telem(true);
|
||||
await sync_and_validate_telem(ping => {
|
||||
Assert.equal(
|
||||
ping.engines.find(e => e.name == "history").incoming.failed,
|
||||
5
|
||||
);
|
||||
});
|
||||
|
||||
Assert.equal(
|
||||
ping.engines.find(e => e.name == "history").incoming.failed,
|
||||
5
|
||||
);
|
||||
_(
|
||||
"Keys now: " + Service.collectionKeys.keyForCollection("history").keyPair
|
||||
);
|
||||
|
@ -63,8 +63,9 @@ add_task(async function test_401_logout() {
|
||||
Service._updateCachedURLs();
|
||||
|
||||
_("Starting first sync.");
|
||||
let ping = await sync_and_validate_telem(true);
|
||||
deepEqual(ping.failureReason, { name: "httperror", code: 401 });
|
||||
await sync_and_validate_telem(ping => {
|
||||
deepEqual(ping.failureReason, { name: "httperror", code: 401 });
|
||||
});
|
||||
_("First sync done.");
|
||||
|
||||
await promiseErrors;
|
||||
@ -89,11 +90,12 @@ add_task(async function test_credentials_changed_logout() {
|
||||
|
||||
await EHTestsCommon.generateCredentialsChangedFailure();
|
||||
|
||||
let ping = await sync_and_validate_telem(true);
|
||||
equal(ping.status.sync, CREDENTIALS_CHANGED);
|
||||
deepEqual(ping.failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: "Error: Aborting sync, remote setup failed",
|
||||
await sync_and_validate_telem(ping => {
|
||||
equal(ping.status.sync, CREDENTIALS_CHANGED);
|
||||
deepEqual(ping.failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: "Error: Aborting sync, remote setup failed",
|
||||
});
|
||||
});
|
||||
|
||||
Assert.equal(Status.sync, CREDENTIALS_CHANGED);
|
||||
@ -135,11 +137,12 @@ add_task(async function test_sync_non_network_error() {
|
||||
|
||||
await EHTestsCommon.generateCredentialsChangedFailure();
|
||||
|
||||
let ping = await sync_and_validate_telem(true);
|
||||
equal(ping.status.sync, CREDENTIALS_CHANGED);
|
||||
deepEqual(ping.failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: "Error: Aborting sync, remote setup failed",
|
||||
await sync_and_validate_telem(ping => {
|
||||
equal(ping.status.sync, CREDENTIALS_CHANGED);
|
||||
deepEqual(ping.failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: "Error: Aborting sync, remote setup failed",
|
||||
});
|
||||
});
|
||||
|
||||
Assert.equal(Status.sync, CREDENTIALS_CHANGED);
|
||||
@ -252,11 +255,12 @@ add_task(async function test_sync_server_maintenance_error() {
|
||||
|
||||
Assert.equal(Status.service, STATUS_OK);
|
||||
|
||||
let ping = await sync_and_validate_telem(true);
|
||||
equal(ping.status.sync, SERVER_MAINTENANCE);
|
||||
deepEqual(ping.engines.find(e => e.failureReason).failureReason, {
|
||||
name: "httperror",
|
||||
code: 503,
|
||||
await sync_and_validate_telem(ping => {
|
||||
equal(ping.status.sync, SERVER_MAINTENANCE);
|
||||
deepEqual(ping.engines.find(e => e.failureReason).failureReason, {
|
||||
name: "httperror",
|
||||
code: 503,
|
||||
});
|
||||
});
|
||||
|
||||
Assert.equal(Status.service, SYNC_FAILED_PARTIAL);
|
||||
|
@ -103,7 +103,7 @@ add_task(async function test_lastSync_not_updated_on_complete_failure() {
|
||||
|
||||
// Do an initial sync that we expect to be successful.
|
||||
let promiseObserved = promiseOneObserver("weave:service:reset-file-log");
|
||||
await sync_and_validate_telem(false);
|
||||
await sync_and_validate_telem();
|
||||
await promiseObserved;
|
||||
|
||||
Assert.equal(Status.service, STATUS_OK);
|
||||
@ -120,7 +120,7 @@ add_task(async function test_lastSync_not_updated_on_complete_failure() {
|
||||
);
|
||||
|
||||
promiseObserved = promiseOneObserver("weave:service:reset-file-log");
|
||||
await sync_and_validate_telem(true);
|
||||
await sync_and_validate_telem(() => {});
|
||||
await promiseObserved;
|
||||
|
||||
Assert.equal(Status.sync, SERVER_MAINTENANCE);
|
||||
@ -419,9 +419,10 @@ add_task(async function test_sync_engine_generic_fail() {
|
||||
});
|
||||
|
||||
Assert.ok(await EHTestsCommon.setUp(server));
|
||||
let ping = await sync_and_validate_telem(true);
|
||||
deepEqual(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
deepEqual(ping.engines.find(e => e.status).status, ENGINE_UNKNOWN_FAIL);
|
||||
await sync_and_validate_telem(ping => {
|
||||
deepEqual(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
deepEqual(ping.engines.find(e => e.status).status, ENGINE_UNKNOWN_FAIL);
|
||||
});
|
||||
|
||||
await promiseObserved;
|
||||
|
||||
|
@ -132,7 +132,7 @@ add_task(async function test_basic() {
|
||||
let server = httpd_setup(handlers);
|
||||
await configureIdentity({ username: "johndoe" }, server);
|
||||
|
||||
let ping = await sync_and_validate_telem(true, true);
|
||||
let ping = await wait_for_ping(() => Service.sync(), true, true);
|
||||
|
||||
// Check the "os" block - we can't really check specific values, but can
|
||||
// check it smells sane.
|
||||
@ -426,11 +426,12 @@ add_task(async function test_generic_engine_fail() {
|
||||
changes
|
||||
)}`
|
||||
);
|
||||
let ping = await sync_and_validate_telem(true);
|
||||
equal(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
deepEqual(ping.engines.find(err => err.name === "steam").failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: String(e),
|
||||
await sync_and_validate_telem(ping => {
|
||||
equal(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
deepEqual(ping.engines.find(err => err.name === "steam").failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: String(e),
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
await cleanAndGo(engine, server);
|
||||
@ -448,18 +449,20 @@ add_task(async function test_engine_fail_weird_errors() {
|
||||
try {
|
||||
let msg = "Bad things happened!";
|
||||
engine._errToThrow = { message: msg };
|
||||
let ping = await sync_and_validate_telem(true);
|
||||
equal(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
deepEqual(ping.engines.find(err => err.name === "steam").failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: "Bad things happened!",
|
||||
await sync_and_validate_telem(ping => {
|
||||
equal(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
deepEqual(ping.engines.find(err => err.name === "steam").failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: "Bad things happened!",
|
||||
});
|
||||
});
|
||||
let e = { msg };
|
||||
engine._errToThrow = e;
|
||||
ping = await sync_and_validate_telem(true);
|
||||
deepEqual(ping.engines.find(err => err.name === "steam").failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: JSON.stringify(e),
|
||||
await sync_and_validate_telem(ping => {
|
||||
deepEqual(ping.engines.find(err => err.name === "steam").failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: JSON.stringify(e),
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
await cleanAndGo(engine, server);
|
||||
@ -485,31 +488,32 @@ add_task(async function test_overrideTelemetryName() {
|
||||
try {
|
||||
info("Sync with validation problems");
|
||||
engine.problemsToReport = problemsToReport;
|
||||
let ping = await sync_and_validate_telem(true);
|
||||
let enginePing = ping.engines.find(e => e.name === "steam-but-better");
|
||||
ok(enginePing);
|
||||
ok(!ping.engines.find(e => e.name === "steam"));
|
||||
|
||||
deepEqual(
|
||||
enginePing.validation,
|
||||
{
|
||||
version: 1,
|
||||
checked: 0,
|
||||
problems: problemsToReport,
|
||||
},
|
||||
"Should include validation report with overridden name"
|
||||
);
|
||||
await sync_and_validate_telem(ping => {
|
||||
let enginePing = ping.engines.find(e => e.name === "steam-but-better");
|
||||
ok(enginePing);
|
||||
ok(!ping.engines.find(e => e.name === "steam"));
|
||||
deepEqual(
|
||||
enginePing.validation,
|
||||
{
|
||||
version: 1,
|
||||
checked: 0,
|
||||
problems: problemsToReport,
|
||||
},
|
||||
"Should include validation report with overridden name"
|
||||
);
|
||||
});
|
||||
|
||||
info("Sync without validation problems");
|
||||
engine.problemsToReport = null;
|
||||
ping = await sync_and_validate_telem(true);
|
||||
enginePing = ping.engines.find(e => e.name === "steam-but-better");
|
||||
ok(enginePing);
|
||||
ok(!ping.engines.find(e => e.name === "steam"));
|
||||
ok(
|
||||
!enginePing.validation,
|
||||
"Should not include validation report when there are no problems"
|
||||
);
|
||||
await sync_and_validate_telem(ping => {
|
||||
let enginePing = ping.engines.find(e => e.name === "steam-but-better");
|
||||
ok(enginePing);
|
||||
ok(!ping.engines.find(e => e.name === "steam"));
|
||||
ok(
|
||||
!enginePing.validation,
|
||||
"Should not include validation report when there are no problems"
|
||||
);
|
||||
});
|
||||
} finally {
|
||||
await cleanAndGo(engine, server);
|
||||
await Service.engineManager.unregister(engine);
|
||||
@ -542,17 +546,18 @@ add_task(async function test_engine_fail_ioerror() {
|
||||
changes
|
||||
)}`
|
||||
);
|
||||
let ping = await sync_and_validate_telem(true);
|
||||
equal(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
let failureReason = ping.engines.find(e => e.name === "steam")
|
||||
.failureReason;
|
||||
equal(failureReason.name, "unexpectederror");
|
||||
// ensure the profile dir in the exception message has been stripped.
|
||||
ok(
|
||||
!failureReason.error.includes(OS.Constants.Path.profileDir),
|
||||
failureReason.error
|
||||
);
|
||||
ok(failureReason.error.includes("[profileDir]"), failureReason.error);
|
||||
await sync_and_validate_telem(ping => {
|
||||
equal(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
let failureReason = ping.engines.find(e => e.name === "steam")
|
||||
.failureReason;
|
||||
equal(failureReason.name, "unexpectederror");
|
||||
// ensure the profile dir in the exception message has been stripped.
|
||||
ok(
|
||||
!failureReason.error.includes(OS.Constants.Path.profileDir),
|
||||
failureReason.error
|
||||
);
|
||||
ok(failureReason.error.includes("[profileDir]"), failureReason.error);
|
||||
});
|
||||
} finally {
|
||||
await cleanAndGo(engine, server);
|
||||
await Service.engineManager.unregister(engine);
|
||||
@ -574,23 +579,26 @@ add_task(async function test_clean_urls() {
|
||||
try {
|
||||
const changes = await engine._tracker.getChangedIDs();
|
||||
_(`test_clean_urls: Steam tracker contents: ${JSON.stringify(changes)}`);
|
||||
let ping = await sync_and_validate_telem(true);
|
||||
equal(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
let failureReason = ping.engines.find(e => e.name === "steam")
|
||||
.failureReason;
|
||||
equal(failureReason.name, "unexpectederror");
|
||||
equal(failureReason.error, "<URL> is not a valid URL.");
|
||||
await sync_and_validate_telem(ping => {
|
||||
equal(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
let failureReason = ping.engines.find(e => e.name === "steam")
|
||||
.failureReason;
|
||||
equal(failureReason.name, "unexpectederror");
|
||||
equal(failureReason.error, "<URL> is not a valid URL.");
|
||||
});
|
||||
// Handle other errors that include urls.
|
||||
engine._errToThrow =
|
||||
"Other error message that includes some:url/foo/bar/ in it.";
|
||||
ping = await sync_and_validate_telem(true);
|
||||
equal(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
failureReason = ping.engines.find(e => e.name === "steam").failureReason;
|
||||
equal(failureReason.name, "unexpectederror");
|
||||
equal(
|
||||
failureReason.error,
|
||||
"Other error message that includes <URL> in it."
|
||||
);
|
||||
await sync_and_validate_telem(ping => {
|
||||
equal(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
let failureReason = ping.engines.find(e => e.name === "steam")
|
||||
.failureReason;
|
||||
equal(failureReason.name, "unexpectederror");
|
||||
equal(
|
||||
failureReason.error,
|
||||
"Other error message that includes <URL> in it."
|
||||
);
|
||||
});
|
||||
} finally {
|
||||
await cleanAndGo(engine, server);
|
||||
await Service.engineManager.unregister(engine);
|
||||
@ -659,15 +667,16 @@ add_task(async function test_nserror() {
|
||||
try {
|
||||
const changes = await engine._tracker.getChangedIDs();
|
||||
_(`test_nserror: Steam tracker contents: ${JSON.stringify(changes)}`);
|
||||
let ping = await sync_and_validate_telem(true);
|
||||
deepEqual(ping.status, {
|
||||
service: SYNC_FAILED_PARTIAL,
|
||||
sync: LOGIN_FAILED_NETWORK_ERROR,
|
||||
});
|
||||
let enginePing = ping.engines.find(e => e.name === "steam");
|
||||
deepEqual(enginePing.failureReason, {
|
||||
name: "nserror",
|
||||
code: Cr.NS_ERROR_UNKNOWN_HOST,
|
||||
await sync_and_validate_telem(ping => {
|
||||
deepEqual(ping.status, {
|
||||
service: SYNC_FAILED_PARTIAL,
|
||||
sync: LOGIN_FAILED_NETWORK_ERROR,
|
||||
});
|
||||
let enginePing = ping.engines.find(e => e.name === "steam");
|
||||
deepEqual(enginePing.failureReason, {
|
||||
name: "nserror",
|
||||
code: Cr.NS_ERROR_UNKNOWN_HOST,
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
await cleanAndGo(engine, server);
|
||||
@ -757,7 +766,7 @@ add_task(async function test_discarding() {
|
||||
}
|
||||
telem.submit = oldSubmit;
|
||||
telem.submissionInterval = -1;
|
||||
let ping = await sync_and_validate_telem(true, true); // with this we've synced 6 times
|
||||
let ping = await wait_for_ping(() => Service.sync(), true, true); // with this we've synced 6 times
|
||||
equal(ping.syncs.length, 2);
|
||||
equal(ping.discarded, 4);
|
||||
} finally {
|
||||
@ -770,6 +779,39 @@ add_task(async function test_discarding() {
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_submit_interval() {
|
||||
let telem = get_sync_test_telemetry();
|
||||
let oldSubmit = telem.submit;
|
||||
let numSubmissions = 0;
|
||||
telem.submit = function() {
|
||||
numSubmissions += 1;
|
||||
};
|
||||
|
||||
function notify(what, data = null) {
|
||||
Svc.Obs.notify(what, JSON.stringify(data));
|
||||
}
|
||||
|
||||
try {
|
||||
// submissionInterval is set such that each sync should submit
|
||||
notify("weave:service:sync:start", { why: "testing" });
|
||||
notify("weave:service:sync:finish");
|
||||
Assert.equal(numSubmissions, 1, "should submit this ping due to interval");
|
||||
|
||||
// As should each event outside of a sync.
|
||||
Service.recordTelemetryEvent("object", "method");
|
||||
Assert.equal(numSubmissions, 2);
|
||||
|
||||
// But events while we are syncing should not.
|
||||
notify("weave:service:sync:start", { why: "testing" });
|
||||
Service.recordTelemetryEvent("object", "method");
|
||||
Assert.equal(numSubmissions, 2, "no submission for this event");
|
||||
notify("weave:service:sync:finish");
|
||||
Assert.equal(numSubmissions, 3, "was submitted after sync finish");
|
||||
} finally {
|
||||
telem.submit = oldSubmit;
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_no_foreign_engines_in_error_ping() {
|
||||
enableValidationPrefs();
|
||||
|
||||
@ -780,9 +822,10 @@ add_task(async function test_no_foreign_engines_in_error_ping() {
|
||||
engine._errToThrow = new Error("Oh no!");
|
||||
await SyncTestingInfrastructure(server);
|
||||
try {
|
||||
let ping = await sync_and_validate_telem(true);
|
||||
equal(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
ok(ping.engines.every(e => e.name !== "bogus"));
|
||||
await sync_and_validate_telem(ping => {
|
||||
equal(ping.status.service, SYNC_FAILED_PARTIAL);
|
||||
ok(ping.engines.every(e => e.name !== "bogus"));
|
||||
});
|
||||
} finally {
|
||||
await cleanAndGo(engine, server);
|
||||
await Service.engineManager.unregister(engine);
|
||||
@ -799,8 +842,9 @@ add_task(async function test_no_foreign_engines_in_success_ping() {
|
||||
|
||||
await SyncTestingInfrastructure(server);
|
||||
try {
|
||||
let ping = await sync_and_validate_telem();
|
||||
ok(ping.engines.every(e => e.name !== "bogus"));
|
||||
await sync_and_validate_telem(ping => {
|
||||
ok(ping.engines.every(e => e.name !== "bogus"));
|
||||
});
|
||||
} finally {
|
||||
await cleanAndGo(engine, server);
|
||||
await Service.engineManager.unregister(engine);
|
||||
@ -816,6 +860,10 @@ add_task(async function test_events() {
|
||||
let server = await serverForFoo(engine);
|
||||
|
||||
await SyncTestingInfrastructure(server);
|
||||
|
||||
let telem = get_sync_test_telemetry();
|
||||
telem.submissionInterval = Infinity;
|
||||
|
||||
try {
|
||||
let serverTime = Resource.serverTime;
|
||||
Service.recordTelemetryEvent("object", "method", "value", { foo: "bar" });
|
||||
@ -828,28 +876,53 @@ add_task(async function test_events() {
|
||||
equal(object, "object");
|
||||
equal(value, "value");
|
||||
deepEqual(extra, { foo: "bar", serverTime: String(serverTime) });
|
||||
// Test with optional values.
|
||||
Service.recordTelemetryEvent("object", "method");
|
||||
ping = await wait_for_ping(() => Service.sync(), false, true);
|
||||
ping = await wait_for_ping(
|
||||
() => {
|
||||
// Test with optional values.
|
||||
Service.recordTelemetryEvent("object", "method");
|
||||
},
|
||||
false,
|
||||
true
|
||||
);
|
||||
equal(ping.events.length, 1);
|
||||
equal(ping.events[0].length, 4);
|
||||
|
||||
Service.recordTelemetryEvent("object", "method", "extra");
|
||||
ping = await wait_for_ping(() => Service.sync(), false, true);
|
||||
ping = await wait_for_ping(
|
||||
() => {
|
||||
Service.recordTelemetryEvent("object", "method", "extra");
|
||||
},
|
||||
false,
|
||||
true
|
||||
);
|
||||
equal(ping.events.length, 1);
|
||||
equal(ping.events[0].length, 5);
|
||||
|
||||
Service.recordTelemetryEvent("object", "method", undefined, { foo: "bar" });
|
||||
ping = await wait_for_ping(() => Service.sync(), false, true);
|
||||
ping = await wait_for_ping(
|
||||
() => {
|
||||
Service.recordTelemetryEvent("object", "method", undefined, {
|
||||
foo: "bar",
|
||||
});
|
||||
},
|
||||
false,
|
||||
true
|
||||
);
|
||||
equal(ping.events.length, 1);
|
||||
equal(ping.events[0].length, 6);
|
||||
[timestamp, category, method, object, value, extra] = ping.events[0];
|
||||
equal(value, null);
|
||||
|
||||
Service.recordTelemetryEvent("object", "method", undefined, { foo: "bar" });
|
||||
let telem = get_sync_test_telemetry();
|
||||
// Fake a submission due to shutdown.
|
||||
ping = await wait_for_ping(() => telem.finish("shutdown"), false, true);
|
||||
ping = await wait_for_ping(
|
||||
() => {
|
||||
telem.submissionInterval = Infinity;
|
||||
Service.recordTelemetryEvent("object", "method", undefined, {
|
||||
foo: "bar",
|
||||
});
|
||||
telem.finish("shutdown");
|
||||
},
|
||||
false,
|
||||
true
|
||||
);
|
||||
equal(ping.syncs.length, 0);
|
||||
equal(ping.events.length, 1);
|
||||
equal(ping.events[0].length, 6);
|
||||
|
Loading…
Reference in New Issue
Block a user