summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/test/xpcshell/test_ext_startup_cache_telemetry.js
blob: 7ceec46679a505ee9884ee4541ab30e01384f033 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

ChromeUtils.defineESModuleGetters(this, {
  ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
  TelemetryTestUtils: "resource://testing-common/TelemetryTestUtils.sys.mjs",
});

AddonTestUtils.init(this);
AddonTestUtils.overrideCertDB();

AddonTestUtils.createAppInfo(
  "xpcshell@tests.mozilla.org",
  "XPCShell",
  "42",
  "42"
);

const ADDON_ID = "test-startup-cache-telemetry@xpcshell.mozilla.org";

add_setup(async () => {
  // FOG needs a profile directory to put its data in.
  do_get_profile();
  // FOG needs to be initialized in order for data to flow.
  Services.fog.initializeFOG();

  await AddonTestUtils.promiseStartupManager();
});

add_task(async function test_startupCache_write_byteLength() {
  Services.fog.testResetFOG();
  const extension = ExtensionTestUtils.loadExtension({
    useAddonManager: "permanent",
    manifest: {
      browser_specific_settings: { gecko: { id: ADDON_ID } },
    },
  });

  await extension.startup();

  const { StartupCache } = ExtensionParent;

  const aomStartup = Cc[
    "@mozilla.org/addons/addon-manager-startup;1"
  ].getService(Ci.amIAddonManagerStartup);

  let expectedByteLength = new Uint8Array(
    aomStartup.encodeBlob(StartupCache._data)
  ).byteLength;

  equal(
    typeof expectedByteLength,
    "number",
    "Got a numeric byteLength for the expected startupCache data"
  );
  Assert.greater(
    expectedByteLength,
    0,
    "Got a non-zero byteLength as expected"
  );
  await StartupCache._saveNow();

  let scalars = TelemetryTestUtils.getProcessScalars("parent");
  equal(
    scalars["extensions.startupCache.write_byteLength"],
    expectedByteLength,
    "Got the expected value set in the 'extensions.startupCache.write_byteLength' scalar"
  );
  equal(
    Glean.extensions.startupCacheWriteBytelength.testGetValue(),
    expectedByteLength,
    "Expected 'extensions.startupCache.write_byteLength' Glean metric."
  );

  await extension.unload();
});

add_task(async function test_startupCache_read_errors() {
  const { StartupCache } = ExtensionParent;

  // Clear any pre-existing keyed scalar.
  TelemetryTestUtils.getProcessScalars("parent", /* keyed */ true, true);
  Services.fog.testResetFOG();

  // Temporarily point StartupCache._file to a path that is
  // not going to exist for sure.
  Assert.notEqual(
    StartupCache.file,
    null,
    "Got a StartupCache._file non-null property as expected"
  );
  const oldFile = StartupCache.file;
  const restoreStartupCacheFile = () => (StartupCache.file = oldFile);
  StartupCache.file = `${StartupCache.file}.non_existing_file.${Math.random()}`;
  registerCleanupFunction(restoreStartupCacheFile);

  // Make sure the _readData has been called and we can expect
  // the extensions.startupCache.read_errors scalar to have
  // been recorded.
  await StartupCache._readData();

  let scalars = TelemetryTestUtils.getProcessScalars(
    "parent",
    /* keyed */ true
  );
  Assert.deepEqual(
    scalars["extensions.startupCache.read_errors"],
    {
      NotFoundError: 1,
    },
    "Got the expected value set in the 'extensions.startupCache.read_errors' keyed scalar"
  );
  Assert.deepEqual(
    Glean.extensions.startupCacheReadErrors.NotFoundError.testGetValue(),
    1,
    "Expected value for 'extensions.startupCache.read_errors' Glean metric."
  );

  restoreStartupCacheFile();
});

add_task(async function test_startupCache_load_timestamps() {
  const { StartupCache } = ExtensionParent;

  // Clear any pre-existing keyed scalar and Glean metrics data.
  Services.telemetry.getSnapshotForScalars("main", true);
  Services.fog.testResetFOG();

  let gleanMetric = Glean.extensions.startupCacheLoadTime.testGetValue();
  equal(
    gleanMetric,
    null,
    "Expect extensions.startup_cache_load_time Glean metric to be initially null"
  );

  // Make sure the _readData has been called and we can expect
  // the startupCache load telemetry timestamps to have been
  // recorded.
  await StartupCache._readData();

  info(
    "Verify telemetry recorded for the 'extensions.startup_cache_load_time' Glean metric"
  );

  gleanMetric = Glean.extensions.startupCacheLoadTime.testGetValue();
  equal(
    typeof gleanMetric,
    "number",
    "Expect extensions.startup_cache_load_time Glean metric to be set to a number"
  );

  info(
    "Verify telemetry mirrored into the 'extensions.startupCache.load_time' scalar"
  );

  const scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  const mirror = scalars["extensions.startupCache.load_time"];

  equal(
    typeof mirror,
    "number",
    "Expect extensions.startupCache.load_time mirrored scalar to be set to a number"
  );

  // See https://bugzilla.mozilla.org/show_bug.cgi?id=1865850.
  Assert.lessOrEqual(
    Math.abs(gleanMetric - mirror),
    1,
    `Expect Glean metric ${gleanMetric} and mirrored scalar ${mirror} to be within 1ms of each other.`
  );
});