summaryrefslogtreecommitdiffstats
path: root/dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js')
-rw-r--r--dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js866
1 files changed, 866 insertions, 0 deletions
diff --git a/dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js b/dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js
new file mode 100644
index 0000000000..9a3afba8c5
--- /dev/null
+++ b/dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js
@@ -0,0 +1,866 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const { AppConstants } = ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+const { TelemetryTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TelemetryTestUtils.sys.mjs"
+);
+
+const storageDirName = "storage";
+const storageFileName = "storage.sqlite";
+const indexedDBDirName = "indexedDB";
+const persistentStorageDirName = "storage/persistent";
+const histogramName = "QM_FIRST_INITIALIZATION_ATTEMPT";
+
+const testcases = [
+ {
+ mainKey: "Storage",
+ async setup(expectedInitResult) {
+ if (!expectedInitResult) {
+ // Make the database unusable by creating it as a directory (not a
+ // file).
+ const storageFile = getRelativeFile(storageFileName);
+ storageFile.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "TemporaryStorage",
+ async setup(expectedInitResult) {
+ // We need to initialize storage before populating the repositories. If
+ // we don't do that, the storage directory created for the repositories
+ // would trigger storage upgrades (from version 0 to current version).
+ let request = init();
+ await requestFinished(request);
+
+ populateRepository("temporary");
+ populateRepository("default");
+
+ if (!expectedInitResult) {
+ makeRepositoryUnusable("temporary");
+ makeRepositoryUnusable("default");
+ }
+ },
+ initFunction: initTemporaryStorage,
+ getExpectedSnapshots() {
+ const expectedSnapshotsInNightly = {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ TemporaryRepository: {
+ values: [1, 0],
+ },
+ DefaultRepository: {
+ values: [1, 0],
+ },
+ // mainKey
+ TemporaryStorage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ TemporaryRepository: {
+ values: [1, 1, 0],
+ },
+ DefaultRepository: {
+ values: [1, 1, 0],
+ },
+ // mainKey
+ TemporaryStorage: {
+ values: [1, 1, 0],
+ },
+ },
+ };
+
+ const expectedSnapshotsInOthers = {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ TemporaryRepository: {
+ values: [1, 0],
+ },
+ // mainKey
+ TemporaryStorage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ TemporaryRepository: {
+ values: [1, 1, 0],
+ },
+ DefaultRepository: {
+ values: [0, 1, 0],
+ },
+ // mainKey
+ TemporaryStorage: {
+ values: [1, 1, 0],
+ },
+ },
+ };
+
+ return AppConstants.NIGHTLY_BUILD
+ ? expectedSnapshotsInNightly
+ : expectedSnapshotsInOthers;
+ },
+ },
+ {
+ mainKey: "DefaultRepository",
+ async setup(expectedInitResult) {
+ // See the comment for "TemporaryStorage".
+ let request = init();
+ await requestFinished(request);
+
+ populateRepository("default");
+
+ if (!expectedInitResult) {
+ makeRepositoryUnusable("default");
+ }
+ },
+ initFunction: initTemporaryStorage,
+ expectedSnapshots: {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ TemporaryRepository: {
+ values: [0, 1, 0],
+ },
+ // mainKey
+ DefaultRepository: {
+ values: [1, 0],
+ },
+ TemporaryStorage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ TemporaryRepository: {
+ values: [0, 2, 0],
+ },
+ // mainKey
+ DefaultRepository: {
+ values: [1, 1, 0],
+ },
+ TemporaryStorage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "TemporaryRepository",
+ async setup(expectedInitResult) {
+ // See the comment for "TemporaryStorage".
+ let request = init();
+ await requestFinished(request);
+
+ populateRepository("temporary");
+
+ if (!expectedInitResult) {
+ makeRepositoryUnusable("temporary");
+ }
+ },
+ initFunction: initTemporaryStorage,
+ getExpectedSnapshots() {
+ const expectedSnapshotsInNightly = {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ // mainKey
+ TemporaryRepository: {
+ values: [1, 0],
+ },
+ DefaultRepository: {
+ values: [0, 1, 0],
+ },
+ TemporaryStorage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ // mainKey
+ TemporaryRepository: {
+ values: [1, 1, 0],
+ },
+ DefaultRepository: {
+ values: [0, 2, 0],
+ },
+ TemporaryStorage: {
+ values: [1, 1, 0],
+ },
+ },
+ };
+
+ const expectedSnapshotsInOthers = {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ // mainKey
+ TemporaryRepository: {
+ values: [1, 0],
+ },
+ TemporaryStorage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ // mainKey
+ TemporaryRepository: {
+ values: [1, 1, 0],
+ },
+ DefaultRepository: {
+ values: [0, 1, 0],
+ },
+ TemporaryStorage: {
+ values: [1, 1, 0],
+ },
+ },
+ };
+
+ return AppConstants.NIGHTLY_BUILD
+ ? expectedSnapshotsInNightly
+ : expectedSnapshotsInOthers;
+ },
+ },
+ {
+ mainKey: "UpgradeStorageFrom0_0To1_0",
+ async setup(expectedInitResult) {
+ // storage used prior FF 49 (storage version 0.0)
+ installPackage("version0_0_profile");
+
+ if (!expectedInitResult) {
+ installPackage("version0_0_make_it_unusable");
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeStorageFrom0_0To1_0: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeStorageFrom0_0To1_0: {
+ values: [1, 1, 0],
+ },
+ UpgradeStorageFrom1_0To2_0: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_0To2_1: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_1To2_2: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_2To2_3: {
+ values: [0, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "UpgradeStorageFrom1_0To2_0",
+ async setup(expectedInitResult) {
+ // storage used by FF 49-54 (storage version 1.0)
+ installPackage("version1_0_profile");
+
+ if (!expectedInitResult) {
+ installPackage("version1_0_make_it_unusable");
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeStorageFrom1_0To2_0: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeStorageFrom1_0To2_0: {
+ values: [1, 1, 0],
+ },
+ UpgradeStorageFrom2_0To2_1: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_1To2_2: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_2To2_3: {
+ values: [0, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "UpgradeStorageFrom2_0To2_1",
+ async setup(expectedInitResult) {
+ // storage used by FF 55-56 (storage version 2.0)
+ installPackage("version2_0_profile");
+
+ if (!expectedInitResult) {
+ installPackage("version2_0_make_it_unusable");
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeStorageFrom2_0To2_1: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeStorageFrom2_0To2_1: {
+ values: [1, 1, 0],
+ },
+ UpgradeStorageFrom2_1To2_2: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_2To2_3: {
+ values: [0, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "UpgradeStorageFrom2_1To2_2",
+ async setup(expectedInitResult) {
+ // storage used by FF 57-67 (storage version 2.1)
+ installPackage("version2_1_profile");
+
+ if (!expectedInitResult) {
+ installPackage("version2_1_make_it_unusable");
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeStorageFrom2_1To2_2: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeStorageFrom2_1To2_2: {
+ values: [1, 1, 0],
+ },
+ UpgradeStorageFrom2_2To2_3: {
+ values: [0, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "UpgradeStorageFrom2_2To2_3",
+ async setup(expectedInitResult) {
+ // storage used by FF 68-69 (storage version 2.2)
+ installPackage("version2_2_profile");
+
+ if (!expectedInitResult) {
+ installPackage(
+ "version2_2_make_it_unusable",
+ /* allowFileOverwrites */ true
+ );
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeStorageFrom2_2To2_3: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeStorageFrom2_2To2_3: {
+ values: [1, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "UpgradeFromIndexedDBDirectory",
+ async setup(expectedInitResult) {
+ const indexedDBDir = getRelativeFile(indexedDBDirName);
+ indexedDBDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+
+ if (!expectedInitResult) {
+ // "indexedDB" directory will be moved under "storage" directory and at
+ // the same time renamed to "persistent". Create a storage file to cause
+ // the moves to fail.
+ const storageFile = getRelativeFile(storageDirName);
+ storageFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeFromIndexedDBDirectory: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeFromIndexedDBDirectory: {
+ values: [1, 1, 0],
+ },
+ UpgradeFromPersistentStorageDirectory: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom0_0To1_0: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom1_0To2_0: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_0To2_1: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_1To2_2: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_2To2_3: {
+ values: [0, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "UpgradeFromPersistentStorageDirectory",
+ async setup(expectedInitResult) {
+ const persistentStorageDir = getRelativeFile(persistentStorageDirName);
+ persistentStorageDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+
+ if (!expectedInitResult) {
+ // Create a metadata directory to break creating or upgrading directory
+ // metadata files.
+ const metadataDir = getRelativeFile(
+ "storage/persistent/https+++bad.example.com/.metadata"
+ );
+ metadataDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ }
+ },
+ initFunction: init,
+ expectedSnapshots: {
+ initFailure: {
+ // mainKey
+ UpgradeFromPersistentStorageDirectory: {
+ values: [1, 0],
+ },
+ Storage: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ // mainKey
+ UpgradeFromPersistentStorageDirectory: {
+ values: [1, 1, 0],
+ },
+ UpgradeStorageFrom0_0To1_0: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom1_0To2_0: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_0To2_1: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_1To2_2: {
+ values: [0, 1, 0],
+ },
+ UpgradeStorageFrom2_2To2_3: {
+ values: [0, 1, 0],
+ },
+ Storage: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "PersistentOrigin",
+ async setup(expectedInitResult) {
+ // We need to initialize storage before creating the origin files. If we
+ // don't do that, the storage directory created for the origin files
+ // would trigger storage upgrades (from version 0 to current version).
+ let request = init();
+ await requestFinished(request);
+
+ if (!expectedInitResult) {
+ const originFiles = [
+ getRelativeFile("storage/permanent/https+++example.com"),
+ getRelativeFile("storage/permanent/https+++example1.com"),
+ getRelativeFile("storage/default/https+++example2.com"),
+ ];
+
+ for (const originFile of originFiles) {
+ originFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+ }
+ }
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+ },
+ initFunctions: [
+ {
+ name: initPersistentOrigin,
+ args: [getPrincipal("https://example.com")],
+ },
+ {
+ name: initPersistentOrigin,
+ args: [getPrincipal("https://example1.com")],
+ },
+ {
+ name: initTemporaryOrigin,
+ args: ["default", getPrincipal("https://example2.com")],
+ },
+ ],
+ expectedSnapshots: {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ TemporaryRepository: {
+ values: [0, 1, 0],
+ },
+ DefaultRepository: {
+ values: [0, 1, 0],
+ },
+ TemporaryStorage: {
+ values: [0, 1, 0],
+ },
+ // mainKey
+ PersistentOrigin: {
+ values: [2, 0],
+ },
+ TemporaryOrigin: {
+ values: [1, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ TemporaryRepository: {
+ values: [0, 2, 0],
+ },
+ DefaultRepository: {
+ values: [0, 2, 0],
+ },
+ TemporaryStorage: {
+ values: [0, 2, 0],
+ },
+ // mainKey
+ PersistentOrigin: {
+ values: [2, 2, 0],
+ },
+ TemporaryOrigin: {
+ values: [1, 1, 0],
+ },
+ },
+ },
+ },
+ {
+ mainKey: "TemporaryOrigin",
+ async setup(expectedInitResult) {
+ // See the comment for "PersistentOrigin".
+ let request = init();
+ await requestFinished(request);
+
+ if (!expectedInitResult) {
+ const originFiles = [
+ getRelativeFile("storage/temporary/https+++example.com"),
+ getRelativeFile("storage/default/https+++example.com"),
+ getRelativeFile("storage/default/https+++example1.com"),
+ getRelativeFile("storage/permanent/https+++example2.com"),
+ ];
+
+ for (const originFile of originFiles) {
+ originFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+ }
+ }
+
+ request = initTemporaryStorage();
+ await requestFinished(request);
+ },
+ initFunctions: [
+ {
+ name: initTemporaryOrigin,
+ args: ["temporary", getPrincipal("https://example.com")],
+ },
+ {
+ name: initTemporaryOrigin,
+ args: ["default", getPrincipal("https://example.com")],
+ },
+ {
+ name: initTemporaryOrigin,
+ args: ["default", getPrincipal("https://example1.com")],
+ },
+ {
+ name: initPersistentOrigin,
+ args: [getPrincipal("https://example2.com")],
+ },
+ ],
+ // Only the first result of EnsureTemporaryOriginIsInitialized per origin
+ // should be reported. Thus, only the results for (temporary, example.com),
+ // and (default, example1.com) should be reported.
+ expectedSnapshots: {
+ initFailure: {
+ Storage: {
+ values: [0, 1, 0],
+ },
+ TemporaryRepository: {
+ values: [0, 1, 0],
+ },
+ DefaultRepository: {
+ values: [0, 1, 0],
+ },
+ TemporaryStorage: {
+ values: [0, 1, 0],
+ },
+ PersistentOrigin: {
+ values: [1, 0],
+ },
+ // mainKey
+ TemporaryOrigin: {
+ values: [2, 0],
+ },
+ },
+ initFailureThenSuccess: {
+ Storage: {
+ values: [0, 2, 0],
+ },
+ TemporaryRepository: {
+ values: [0, 2, 0],
+ },
+ DefaultRepository: {
+ values: [0, 2, 0],
+ },
+ TemporaryStorage: {
+ values: [0, 2, 0],
+ },
+ PersistentOrigin: {
+ values: [1, 1, 0],
+ },
+ // mainKey
+ TemporaryOrigin: {
+ values: [2, 2, 0],
+ },
+ },
+ },
+ },
+];
+
+loadScript("dom/quota/test/xpcshell/common/utils.js");
+
+function verifyHistogram(histogram, mainKey, expectedSnapshot) {
+ const snapshot = histogram.snapshot();
+
+ ok(
+ mainKey in snapshot,
+ `The histogram ${histogram.name()} must contain the main key ${mainKey}`
+ );
+
+ const keys = Object.keys(snapshot);
+
+ is(
+ keys.length,
+ Object.keys(expectedSnapshot).length,
+ `The number of keys must match the expected number of keys for ` +
+ `${histogram.name()}`
+ );
+
+ for (const key of keys) {
+ ok(
+ key in expectedSnapshot,
+ `The key ${key} must match the expected keys for ${histogram.name()}`
+ );
+
+ const values = Object.entries(snapshot[key].values);
+ const expectedValues = expectedSnapshot[key].values;
+
+ is(
+ values.length,
+ expectedValues.length,
+ `The number of values should match the expected number of values for ` +
+ `${histogram.name()}`
+ );
+
+ for (let [i, val] of values) {
+ is(
+ val,
+ expectedValues[i],
+ `Expected counts should match for ${histogram.name()} at index ${i}`
+ );
+ }
+ }
+}
+
+async function testSteps() {
+ let request;
+ for (const testcase of testcases) {
+ const mainKey = testcase.mainKey;
+
+ info(`Verifying ${histogramName} histogram for the main key ${mainKey}`);
+
+ const histogram =
+ TelemetryTestUtils.getAndClearKeyedHistogram(histogramName);
+
+ for (const expectedInitResult of [false, true]) {
+ info(
+ `Verifying the histogram when the initialization ` +
+ `${expectedInitResult ? "failed and then succeeds" : "fails"}`
+ );
+
+ await testcase.setup(expectedInitResult);
+
+ const msg = `Should ${expectedInitResult ? "not " : ""} have thrown`;
+
+ // Call the initialization function twice, so we can verify below that
+ // only the first initialization attempt has been reported.
+ for (let i = 0; i < 2; ++i) {
+ let initFunctions;
+
+ if (testcase.initFunctions) {
+ initFunctions = testcase.initFunctions;
+ } else {
+ initFunctions = [
+ {
+ name: testcase.initFunction,
+ args: [],
+ },
+ ];
+ }
+
+ for (const initFunction of initFunctions) {
+ request = initFunction.name(...initFunction.args);
+ try {
+ await requestFinished(request);
+ ok(expectedInitResult, msg);
+ } catch (ex) {
+ ok(!expectedInitResult, msg);
+ }
+ }
+ }
+
+ const expectedSnapshots = testcase.getExpectedSnapshots
+ ? testcase.getExpectedSnapshots()
+ : testcase.expectedSnapshots;
+
+ const expectedSnapshot = expectedInitResult
+ ? expectedSnapshots.initFailureThenSuccess
+ : expectedSnapshots.initFailure;
+
+ verifyHistogram(histogram, mainKey, expectedSnapshot);
+
+ // The first initialization attempt has been reported in the histogram
+ // and any new attemps wouldn't be reported if we didn't reset or clear
+ // the storage here. We need a clean profile for the next iteration
+ // anyway.
+ // However, the clear storage operation needs initialized storage, so
+ // clearing can fail if the storage is unusable and it can also increase
+ // some of the telemetry counters. Instead of calling clear, we can just
+ // call reset and clear profile manually.
+ request = reset();
+ await requestFinished(request);
+
+ const indexedDBDir = getRelativeFile(indexedDBDirName);
+ if (indexedDBDir.exists()) {
+ indexedDBDir.remove(false);
+ }
+
+ const storageDir = getRelativeFile(storageDirName);
+ if (storageDir.exists()) {
+ storageDir.remove(true);
+ }
+
+ const storageFile = getRelativeFile(storageFileName);
+ if (storageFile.exists()) {
+ // It could be a non empty directory, so remove it recursively.
+ storageFile.remove(true);
+ }
+ }
+ }
+}