summaryrefslogtreecommitdiffstats
path: root/browser/components/backup/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
commitd8bbc7858622b6d9c278469aab701ca0b609cddf (patch)
treeeff41dc61d9f714852212739e6b3738b82a2af87 /browser/components/backup/tests
parentReleasing progress-linux version 125.0.3-1~progress7.99u1. (diff)
downloadfirefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz
firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/backup/tests')
-rw-r--r--browser/components/backup/tests/xpcshell/head.js167
-rw-r--r--browser/components/backup/tests/xpcshell/test_BackupResource.js (renamed from browser/components/backup/tests/xpcshell/test_BrowserResource.js)26
-rw-r--r--browser/components/backup/tests/xpcshell/test_MiscDataBackupResource.js113
-rw-r--r--browser/components/backup/tests/xpcshell/test_PlacesBackupResource.js226
-rw-r--r--browser/components/backup/tests/xpcshell/test_PreferencesBackupResource.js132
-rw-r--r--browser/components/backup/tests/xpcshell/test_createBackup.js74
-rw-r--r--browser/components/backup/tests/xpcshell/test_measurements.js547
-rw-r--r--browser/components/backup/tests/xpcshell/xpcshell.toml14
8 files changed, 1287 insertions, 12 deletions
diff --git a/browser/components/backup/tests/xpcshell/head.js b/browser/components/backup/tests/xpcshell/head.js
new file mode 100644
index 0000000000..2402870a13
--- /dev/null
+++ b/browser/components/backup/tests/xpcshell/head.js
@@ -0,0 +1,167 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { BackupService } = ChromeUtils.importESModule(
+ "resource:///modules/backup/BackupService.sys.mjs"
+);
+
+const { BackupResource } = ChromeUtils.importESModule(
+ "resource:///modules/backup/BackupResource.sys.mjs"
+);
+
+const { TelemetryTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TelemetryTestUtils.sys.mjs"
+);
+
+const { Sqlite } = ChromeUtils.importESModule(
+ "resource://gre/modules/Sqlite.sys.mjs"
+);
+
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+
+const BYTES_IN_KB = 1000;
+
+do_get_profile();
+
+/**
+ * Some fake backup resource classes to test with.
+ */
+class FakeBackupResource1 extends BackupResource {
+ static get key() {
+ return "fake1";
+ }
+ static get requiresEncryption() {
+ return false;
+ }
+}
+
+/**
+ * Another fake backup resource class to test with.
+ */
+class FakeBackupResource2 extends BackupResource {
+ static get key() {
+ return "fake2";
+ }
+ static get requiresEncryption() {
+ return true;
+ }
+}
+
+/**
+ * Yet another fake backup resource class to test with.
+ */
+class FakeBackupResource3 extends BackupResource {
+ static get key() {
+ return "fake3";
+ }
+ static get requiresEncryption() {
+ return false;
+ }
+}
+
+/**
+ * Create a file of a given size in kilobytes.
+ *
+ * @param {string} path the path where the file will be created.
+ * @param {number} sizeInKB size file in Kilobytes.
+ * @returns {Promise<undefined>}
+ */
+async function createKilobyteSizedFile(path, sizeInKB) {
+ let bytes = new Uint8Array(sizeInKB * BYTES_IN_KB);
+ await IOUtils.write(path, bytes);
+}
+
+/**
+ * @typedef {object} TestFileObject
+ * @property {(string|Array.<string>)} path
+ * The relative path of the file. It can be a string or an array of strings
+ * in the event that directories need to be created. For example, this is
+ * an array of valid TestFileObjects.
+ *
+ * [
+ * { path: "file1.txt" },
+ * { path: ["dir1", "file2.txt"] },
+ * { path: ["dir2", "dir3", "file3.txt"], sizeInKB: 25 },
+ * { path: "file4.txt" },
+ * ]
+ *
+ * @property {number} [sizeInKB=10]
+ * The size of the created file in kilobytes. Defaults to 10.
+ */
+
+/**
+ * Easily creates a series of test files and directories under parentPath.
+ *
+ * @param {string} parentPath
+ * The path to the parent directory where the files will be created.
+ * @param {TestFileObject[]} testFilesArray
+ * An array of TestFileObjects describing what test files to create within
+ * the parentPath.
+ * @see TestFileObject
+ * @returns {Promise<undefined>}
+ */
+async function createTestFiles(parentPath, testFilesArray) {
+ for (let { path, sizeInKB } of testFilesArray) {
+ if (Array.isArray(path)) {
+ // Make a copy of the array of path elements, chopping off the last one.
+ // We'll assume the unchopped items are directories, and make sure they
+ // exist first.
+ let folders = path.slice(0, -1);
+ await IOUtils.getDirectory(PathUtils.join(parentPath, ...folders));
+ }
+
+ if (sizeInKB === undefined) {
+ sizeInKB = 10;
+ }
+
+ // This little piece of cleverness coerces a string into an array of one
+ // if path is a string, or just leaves it alone if it's already an array.
+ let filePath = PathUtils.join(parentPath, ...[].concat(path));
+ await createKilobyteSizedFile(filePath, sizeInKB);
+ }
+}
+
+/**
+ * Checks that files exist within a particular folder. The filesize is not
+ * checked.
+ *
+ * @param {string} parentPath
+ * The path to the parent directory where the files should exist.
+ * @param {TestFileObject[]} testFilesArray
+ * An array of TestFileObjects describing what test files to search for within
+ * parentPath.
+ * @see TestFileObject
+ * @returns {Promise<undefined>}
+ */
+async function assertFilesExist(parentPath, testFilesArray) {
+ for (let { path } of testFilesArray) {
+ let copiedFileName = PathUtils.join(parentPath, ...[].concat(path));
+ Assert.ok(
+ await IOUtils.exists(copiedFileName),
+ `${copiedFileName} should exist in the staging folder`
+ );
+ }
+}
+
+/**
+ * Remove a file or directory at a path if it exists and files are unlocked.
+ *
+ * @param {string} path path to remove.
+ */
+async function maybeRemovePath(path) {
+ try {
+ await IOUtils.remove(path, { ignoreAbsent: true, recursive: true });
+ } catch (error) {
+ // Sometimes remove() throws when the file is not unlocked soon
+ // enough.
+ if (error.name != "NS_ERROR_FILE_IS_LOCKED") {
+ // Ignoring any errors, as the temp folder will be cleaned up.
+ console.error(error);
+ }
+ }
+}
diff --git a/browser/components/backup/tests/xpcshell/test_BrowserResource.js b/browser/components/backup/tests/xpcshell/test_BackupResource.js
index 23c8e077a5..6623f4cd77 100644
--- a/browser/components/backup/tests/xpcshell/test_BrowserResource.js
+++ b/browser/components/backup/tests/xpcshell/test_BackupResource.js
@@ -3,16 +3,12 @@ http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
-const { BackupResource } = ChromeUtils.importESModule(
+const { bytesToFuzzyKilobytes } = ChromeUtils.importESModule(
"resource:///modules/backup/BackupResource.sys.mjs"
);
const EXPECTED_KILOBYTES_FOR_XULSTORE = 1;
-add_setup(() => {
- do_get_profile();
-});
-
/**
* Tests that BackupService.getFileSize will get the size of a file in kilobytes.
*/
@@ -35,7 +31,7 @@ add_task(async function test_getFileSize() {
});
/**
- * Tests that BackupService.getFileSize will get the total size of all the files in a directory and it's children in kilobytes.
+ * Tests that BackupService.getDirectorySize will get the total size of all the files in a directory and it's children in kilobytes.
*/
add_task(async function test_getDirectorySize() {
let file = do_get_file("data/test_xulstore.json");
@@ -61,3 +57,21 @@ add_task(async function test_getDirectorySize() {
await IOUtils.remove(testDir, { recursive: true });
});
+
+/**
+ * Tests that bytesToFuzzyKilobytes will convert bytes to kilobytes
+ * and round up to the nearest tenth kilobyte.
+ */
+add_task(async function test_bytesToFuzzyKilobytes() {
+ let largeSize = bytesToFuzzyKilobytes(1234000);
+
+ Assert.equal(
+ largeSize,
+ 1230,
+ "1234 bytes is rounded up to the nearest tenth kilobyte, 1230"
+ );
+
+ let smallSize = bytesToFuzzyKilobytes(3);
+
+ Assert.equal(smallSize, 1, "Sizes under 10 kilobytes return 1 kilobyte");
+});
diff --git a/browser/components/backup/tests/xpcshell/test_MiscDataBackupResource.js b/browser/components/backup/tests/xpcshell/test_MiscDataBackupResource.js
new file mode 100644
index 0000000000..e57dd50cd3
--- /dev/null
+++ b/browser/components/backup/tests/xpcshell/test_MiscDataBackupResource.js
@@ -0,0 +1,113 @@
+/* Any copyright is dedicated to the Public Domain.
+https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { MiscDataBackupResource } = ChromeUtils.importESModule(
+ "resource:///modules/backup/MiscDataBackupResource.sys.mjs"
+);
+
+/**
+ * Tests that we can measure miscellaneous files in the profile directory.
+ */
+add_task(async function test_measure() {
+ Services.fog.testResetFOG();
+
+ const EXPECTED_MISC_KILOBYTES_SIZE = 241;
+ const tempDir = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "MiscDataBackupResource-measurement-test"
+ );
+
+ const mockFiles = [
+ { path: "times.json", sizeInKB: 5 },
+ { path: "enumerate_devices.txt", sizeInKB: 1 },
+ { path: "protections.sqlite", sizeInKB: 100 },
+ { path: "SiteSecurityServiceState.bin", sizeInKB: 10 },
+ { path: ["storage", "permanent", "chrome", "123ABC.sqlite"], sizeInKB: 40 },
+ { path: ["storage", "permanent", "chrome", "456DEF.sqlite"], sizeInKB: 40 },
+ {
+ path: ["storage", "permanent", "chrome", "mockIDBDir", "890HIJ.sqlite"],
+ sizeInKB: 40,
+ },
+ ];
+
+ await createTestFiles(tempDir, mockFiles);
+
+ let miscDataBackupResource = new MiscDataBackupResource();
+ await miscDataBackupResource.measure(tempDir);
+
+ let measurement = Glean.browserBackup.miscDataSize.testGetValue();
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
+
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.misc_data_size",
+ measurement,
+ "Glean and telemetry measurements for misc data should be equal"
+ );
+ Assert.equal(
+ measurement,
+ EXPECTED_MISC_KILOBYTES_SIZE,
+ "Should have collected the correct glean measurement for misc files"
+ );
+
+ await maybeRemovePath(tempDir);
+});
+
+add_task(async function test_backup() {
+ let sandbox = sinon.createSandbox();
+
+ let miscDataBackupResource = new MiscDataBackupResource();
+ let sourcePath = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "MiscDataBackupResource-source-test"
+ );
+ let stagingPath = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "MiscDataBackupResource-staging-test"
+ );
+
+ const simpleCopyFiles = [
+ { path: "times.json" },
+ { path: "enumerate_devices.txt" },
+ { path: "SiteSecurityServiceState.bin" },
+ ];
+ await createTestFiles(sourcePath, simpleCopyFiles);
+
+ // We have no need to test that Sqlite.sys.mjs's backup method is working -
+ // this is something that is tested in Sqlite's own tests. We can just make
+ // sure that it's being called using sinon. Unfortunately, we cannot do the
+ // same thing with IOUtils.copy, as its methods are not stubbable.
+ let fakeConnection = {
+ backup: sandbox.stub().resolves(true),
+ close: sandbox.stub().resolves(true),
+ };
+ sandbox.stub(Sqlite, "openConnection").returns(fakeConnection);
+
+ await miscDataBackupResource.backup(stagingPath, sourcePath);
+
+ await assertFilesExist(stagingPath, simpleCopyFiles);
+
+ // Next, we'll make sure that the Sqlite connection had `backup` called on it
+ // with the right arguments.
+ Assert.ok(
+ fakeConnection.backup.calledOnce,
+ "Called backup the expected number of times for all connections"
+ );
+ Assert.ok(
+ fakeConnection.backup.firstCall.calledWith(
+ PathUtils.join(stagingPath, "protections.sqlite")
+ ),
+ "Called backup on the protections.sqlite Sqlite connection"
+ );
+
+ // Bug 1890585 - we don't currently have the ability to copy the
+ // chrome-privileged IndexedDB databases under storage/permanent/chrome, so
+ // we'll just skip testing that for now.
+
+ await maybeRemovePath(stagingPath);
+ await maybeRemovePath(sourcePath);
+
+ sandbox.restore();
+});
diff --git a/browser/components/backup/tests/xpcshell/test_PlacesBackupResource.js b/browser/components/backup/tests/xpcshell/test_PlacesBackupResource.js
new file mode 100644
index 0000000000..de97281372
--- /dev/null
+++ b/browser/components/backup/tests/xpcshell/test_PlacesBackupResource.js
@@ -0,0 +1,226 @@
+/* Any copyright is dedicated to the Public Domain.
+https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { PlacesBackupResource } = ChromeUtils.importESModule(
+ "resource:///modules/backup/PlacesBackupResource.sys.mjs"
+);
+const { PrivateBrowsingUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/PrivateBrowsingUtils.sys.mjs"
+);
+
+const HISTORY_ENABLED_PREF = "places.history.enabled";
+const SANITIZE_ON_SHUTDOWN_PREF = "privacy.sanitize.sanitizeOnShutdown";
+
+registerCleanupFunction(() => {
+ /**
+ * Even though test_backup_no_saved_history clears user prefs too,
+ * clear them here as well in case that test fails and we don't
+ * reach the end of the test, which handles the cleanup.
+ */
+ Services.prefs.clearUserPref(HISTORY_ENABLED_PREF);
+ Services.prefs.clearUserPref(SANITIZE_ON_SHUTDOWN_PREF);
+});
+
+/**
+ * Tests that we can measure Places DB related files in the profile directory.
+ */
+add_task(async function test_measure() {
+ Services.fog.testResetFOG();
+
+ const EXPECTED_PLACES_DB_SIZE = 5240;
+ const EXPECTED_FAVICONS_DB_SIZE = 5240;
+
+ // Create resource files in temporary directory
+ const tempDir = PathUtils.tempDir;
+ let tempPlacesDBPath = PathUtils.join(tempDir, "places.sqlite");
+ let tempFaviconsDBPath = PathUtils.join(tempDir, "favicons.sqlite");
+ await createKilobyteSizedFile(tempPlacesDBPath, EXPECTED_PLACES_DB_SIZE);
+ await createKilobyteSizedFile(tempFaviconsDBPath, EXPECTED_FAVICONS_DB_SIZE);
+
+ let placesBackupResource = new PlacesBackupResource();
+ await placesBackupResource.measure(tempDir);
+
+ let placesMeasurement = Glean.browserBackup.placesSize.testGetValue();
+ let faviconsMeasurement = Glean.browserBackup.faviconsSize.testGetValue();
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
+
+ // Compare glean vs telemetry measurements
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.places_size",
+ placesMeasurement,
+ "Glean and telemetry measurements for places.sqlite should be equal"
+ );
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.favicons_size",
+ faviconsMeasurement,
+ "Glean and telemetry measurements for favicons.sqlite should be equal"
+ );
+
+ // Compare glean measurements vs actual file sizes
+ Assert.equal(
+ placesMeasurement,
+ EXPECTED_PLACES_DB_SIZE,
+ "Should have collected the correct glean measurement for places.sqlite"
+ );
+ Assert.equal(
+ faviconsMeasurement,
+ EXPECTED_FAVICONS_DB_SIZE,
+ "Should have collected the correct glean measurement for favicons.sqlite"
+ );
+
+ await maybeRemovePath(tempPlacesDBPath);
+ await maybeRemovePath(tempFaviconsDBPath);
+});
+
+/**
+ * Tests that the backup method correctly copies places.sqlite and
+ * favicons.sqlite from the profile directory into the staging directory.
+ */
+add_task(async function test_backup() {
+ let sandbox = sinon.createSandbox();
+
+ let placesBackupResource = new PlacesBackupResource();
+ let sourcePath = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "PlacesBackupResource-source-test"
+ );
+ let stagingPath = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "PlacesBackupResource-staging-test"
+ );
+
+ let fakeConnection = {
+ backup: sandbox.stub().resolves(true),
+ close: sandbox.stub().resolves(true),
+ };
+ sandbox.stub(Sqlite, "openConnection").returns(fakeConnection);
+
+ await placesBackupResource.backup(stagingPath, sourcePath);
+
+ Assert.ok(
+ fakeConnection.backup.calledTwice,
+ "Backup should have been called twice"
+ );
+ Assert.ok(
+ fakeConnection.backup.firstCall.calledWith(
+ PathUtils.join(stagingPath, "places.sqlite")
+ ),
+ "places.sqlite should have been backed up first"
+ );
+ Assert.ok(
+ fakeConnection.backup.secondCall.calledWith(
+ PathUtils.join(stagingPath, "favicons.sqlite")
+ ),
+ "favicons.sqlite should have been backed up second"
+ );
+
+ await maybeRemovePath(stagingPath);
+ await maybeRemovePath(sourcePath);
+
+ sandbox.restore();
+});
+
+/**
+ * Tests that the backup method correctly creates a compressed bookmarks JSON file when users
+ * don't want history saved, even on shutdown.
+ */
+add_task(async function test_backup_no_saved_history() {
+ let sandbox = sinon.createSandbox();
+
+ let placesBackupResource = new PlacesBackupResource();
+ let sourcePath = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "PlacesBackupResource-source-test"
+ );
+ let stagingPath = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "PlacesBackupResource-staging-test"
+ );
+
+ let fakeConnection = {
+ backup: sandbox.stub().resolves(true),
+ close: sandbox.stub().resolves(true),
+ };
+ sandbox.stub(Sqlite, "openConnection").returns(fakeConnection);
+
+ /**
+ * First verify that remember history pref alone affects backup file type for places,
+ * despite sanitize on shutdown pref value.
+ */
+ Services.prefs.setBoolPref(HISTORY_ENABLED_PREF, false);
+ Services.prefs.setBoolPref(SANITIZE_ON_SHUTDOWN_PREF, false);
+
+ await placesBackupResource.backup(stagingPath, sourcePath);
+
+ Assert.ok(
+ fakeConnection.backup.notCalled,
+ "No sqlite connections should have been made with remember history disabled"
+ );
+ await assertFilesExist(stagingPath, [{ path: "bookmarks.jsonlz4" }]);
+ await IOUtils.remove(PathUtils.join(stagingPath, "bookmarks.jsonlz4"));
+
+ /**
+ * Now verify that the sanitize shutdown pref alone affects backup file type for places,
+ * even if the user is okay with remembering history while browsing.
+ */
+ Services.prefs.setBoolPref(HISTORY_ENABLED_PREF, true);
+ Services.prefs.setBoolPref(SANITIZE_ON_SHUTDOWN_PREF, true);
+
+ fakeConnection.backup.resetHistory();
+ await placesBackupResource.backup(stagingPath, sourcePath);
+
+ Assert.ok(
+ fakeConnection.backup.notCalled,
+ "No sqlite connections should have been made with sanitize shutdown enabled"
+ );
+ await assertFilesExist(stagingPath, [{ path: "bookmarks.jsonlz4" }]);
+
+ await maybeRemovePath(stagingPath);
+ await maybeRemovePath(sourcePath);
+
+ sandbox.restore();
+ Services.prefs.clearUserPref(HISTORY_ENABLED_PREF);
+ Services.prefs.clearUserPref(SANITIZE_ON_SHUTDOWN_PREF);
+});
+
+/**
+ * Tests that the backup method correctly creates a compressed bookmarks JSON file when
+ * permanent private browsing mode is enabled.
+ */
+add_task(async function test_backup_private_browsing() {
+ let sandbox = sinon.createSandbox();
+
+ let placesBackupResource = new PlacesBackupResource();
+ let sourcePath = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "PlacesBackupResource-source-test"
+ );
+ let stagingPath = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "PlacesBackupResource-staging-test"
+ );
+
+ let fakeConnection = {
+ backup: sandbox.stub().resolves(true),
+ close: sandbox.stub().resolves(true),
+ };
+ sandbox.stub(Sqlite, "openConnection").returns(fakeConnection);
+ sandbox.stub(PrivateBrowsingUtils, "permanentPrivateBrowsing").value(true);
+
+ await placesBackupResource.backup(stagingPath, sourcePath);
+
+ Assert.ok(
+ fakeConnection.backup.notCalled,
+ "No sqlite connections should have been made with permanent private browsing enabled"
+ );
+ await assertFilesExist(stagingPath, [{ path: "bookmarks.jsonlz4" }]);
+
+ await maybeRemovePath(stagingPath);
+ await maybeRemovePath(sourcePath);
+
+ sandbox.restore();
+});
diff --git a/browser/components/backup/tests/xpcshell/test_PreferencesBackupResource.js b/browser/components/backup/tests/xpcshell/test_PreferencesBackupResource.js
new file mode 100644
index 0000000000..6845431bb8
--- /dev/null
+++ b/browser/components/backup/tests/xpcshell/test_PreferencesBackupResource.js
@@ -0,0 +1,132 @@
+/* Any copyright is dedicated to the Public Domain.
+https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { PreferencesBackupResource } = ChromeUtils.importESModule(
+ "resource:///modules/backup/PreferencesBackupResource.sys.mjs"
+);
+
+/**
+ * Test that the measure method correctly collects the disk-sizes of things that
+ * the PreferencesBackupResource is meant to back up.
+ */
+add_task(async function test_measure() {
+ Services.fog.testResetFOG();
+
+ const EXPECTED_PREFERENCES_KILOBYTES_SIZE = 415;
+ const tempDir = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "PreferencesBackupResource-measure-test"
+ );
+ const mockFiles = [
+ { path: "prefs.js", sizeInKB: 20 },
+ { path: "xulstore.json", sizeInKB: 1 },
+ { path: "permissions.sqlite", sizeInKB: 100 },
+ { path: "content-prefs.sqlite", sizeInKB: 260 },
+ { path: "containers.json", sizeInKB: 1 },
+ { path: "handlers.json", sizeInKB: 1 },
+ { path: "search.json.mozlz4", sizeInKB: 1 },
+ { path: "user.js", sizeInKB: 2 },
+ { path: ["chrome", "userChrome.css"], sizeInKB: 5 },
+ { path: ["chrome", "userContent.css"], sizeInKB: 5 },
+ { path: ["chrome", "css", "mockStyles.css"], sizeInKB: 5 },
+ ];
+
+ await createTestFiles(tempDir, mockFiles);
+
+ let preferencesBackupResource = new PreferencesBackupResource();
+
+ await preferencesBackupResource.measure(tempDir);
+
+ let measurement = Glean.browserBackup.preferencesSize.testGetValue();
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
+
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.preferences_size",
+ measurement,
+ "Glean and telemetry measurements for preferences data should be equal"
+ );
+ Assert.equal(
+ measurement,
+ EXPECTED_PREFERENCES_KILOBYTES_SIZE,
+ "Should have collected the correct glean measurement for preferences files"
+ );
+
+ await maybeRemovePath(tempDir);
+});
+
+/**
+ * Test that the backup method correctly copies items from the profile directory
+ * into the staging directory.
+ */
+add_task(async function test_backup() {
+ let sandbox = sinon.createSandbox();
+
+ let preferencesBackupResource = new PreferencesBackupResource();
+ let sourcePath = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "PreferencesBackupResource-source-test"
+ );
+ let stagingPath = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "PreferencesBackupResource-staging-test"
+ );
+
+ const simpleCopyFiles = [
+ { path: "xulstore.json" },
+ { path: "containers.json" },
+ { path: "handlers.json" },
+ { path: "search.json.mozlz4" },
+ { path: "user.js" },
+ { path: ["chrome", "userChrome.css"] },
+ { path: ["chrome", "userContent.css"] },
+ { path: ["chrome", "childFolder", "someOtherStylesheet.css"] },
+ ];
+ await createTestFiles(sourcePath, simpleCopyFiles);
+
+ // We have no need to test that Sqlite.sys.mjs's backup method is working -
+ // this is something that is tested in Sqlite's own tests. We can just make
+ // sure that it's being called using sinon. Unfortunately, we cannot do the
+ // same thing with IOUtils.copy, as its methods are not stubbable.
+ let fakeConnection = {
+ backup: sandbox.stub().resolves(true),
+ close: sandbox.stub().resolves(true),
+ };
+ sandbox.stub(Sqlite, "openConnection").returns(fakeConnection);
+
+ await preferencesBackupResource.backup(stagingPath, sourcePath);
+
+ await assertFilesExist(stagingPath, simpleCopyFiles);
+
+ // Next, we'll make sure that the Sqlite connection had `backup` called on it
+ // with the right arguments.
+ Assert.ok(
+ fakeConnection.backup.calledTwice,
+ "Called backup the expected number of times for all connections"
+ );
+ Assert.ok(
+ fakeConnection.backup.firstCall.calledWith(
+ PathUtils.join(stagingPath, "permissions.sqlite")
+ ),
+ "Called backup on the permissions.sqlite Sqlite connection"
+ );
+ Assert.ok(
+ fakeConnection.backup.secondCall.calledWith(
+ PathUtils.join(stagingPath, "content-prefs.sqlite")
+ ),
+ "Called backup on the content-prefs.sqlite Sqlite connection"
+ );
+
+ // And we'll make sure that preferences were properly written out.
+ Assert.ok(
+ await IOUtils.exists(PathUtils.join(stagingPath, "prefs.js")),
+ "prefs.js should exist in the staging folder"
+ );
+
+ await maybeRemovePath(stagingPath);
+ await maybeRemovePath(sourcePath);
+
+ sandbox.restore();
+});
diff --git a/browser/components/backup/tests/xpcshell/test_createBackup.js b/browser/components/backup/tests/xpcshell/test_createBackup.js
new file mode 100644
index 0000000000..fcace695ef
--- /dev/null
+++ b/browser/components/backup/tests/xpcshell/test_createBackup.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests that calling BackupService.createBackup will call backup on each
+ * registered BackupResource, and that each BackupResource will have a folder
+ * created for them to write into.
+ */
+add_task(async function test_createBackup() {
+ let sandbox = sinon.createSandbox();
+ sandbox
+ .stub(FakeBackupResource1.prototype, "backup")
+ .resolves({ fake1: "hello from 1" });
+ sandbox
+ .stub(FakeBackupResource2.prototype, "backup")
+ .rejects(new Error("Some failure to backup"));
+ sandbox
+ .stub(FakeBackupResource3.prototype, "backup")
+ .resolves({ fake3: "hello from 3" });
+
+ let bs = new BackupService({
+ FakeBackupResource1,
+ FakeBackupResource2,
+ FakeBackupResource3,
+ });
+
+ let fakeProfilePath = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "createBackupTest"
+ );
+
+ await bs.createBackup({ profilePath: fakeProfilePath });
+
+ // For now, we expect a staging folder to exist under the fakeProfilePath,
+ // and we should find a folder for each fake BackupResource.
+ let stagingPath = PathUtils.join(fakeProfilePath, "backups", "staging");
+ Assert.ok(await IOUtils.exists(stagingPath), "Staging folder exists");
+
+ for (let backupResourceClass of [
+ FakeBackupResource1,
+ FakeBackupResource2,
+ FakeBackupResource3,
+ ]) {
+ let expectedResourceFolder = PathUtils.join(
+ stagingPath,
+ backupResourceClass.key
+ );
+ Assert.ok(
+ await IOUtils.exists(expectedResourceFolder),
+ `BackupResource staging folder exists for ${backupResourceClass.key}`
+ );
+ Assert.ok(
+ backupResourceClass.prototype.backup.calledOnce,
+ `Backup was called for ${backupResourceClass.key}`
+ );
+ Assert.ok(
+ backupResourceClass.prototype.backup.calledWith(
+ expectedResourceFolder,
+ fakeProfilePath
+ ),
+ `Backup was passed the right paths for ${backupResourceClass.key}`
+ );
+ }
+
+ // After createBackup is more fleshed out, we're going to want to make sure
+ // that we're writing the manifest file and that it contains the expected
+ // ManifestEntry objects, and that the staging folder was successfully
+ // renamed with the current date.
+ await IOUtils.remove(fakeProfilePath, { recursive: true });
+
+ sandbox.restore();
+});
diff --git a/browser/components/backup/tests/xpcshell/test_measurements.js b/browser/components/backup/tests/xpcshell/test_measurements.js
index e5726126b2..0dece6b370 100644
--- a/browser/components/backup/tests/xpcshell/test_measurements.js
+++ b/browser/components/backup/tests/xpcshell/test_measurements.js
@@ -3,22 +3,59 @@ http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
-const { BackupService } = ChromeUtils.importESModule(
- "resource:///modules/backup/BackupService.sys.mjs"
+const { CredentialsAndSecurityBackupResource } = ChromeUtils.importESModule(
+ "resource:///modules/backup/CredentialsAndSecurityBackupResource.sys.mjs"
+);
+const { AddonsBackupResource } = ChromeUtils.importESModule(
+ "resource:///modules/backup/AddonsBackupResource.sys.mjs"
+);
+const { CookiesBackupResource } = ChromeUtils.importESModule(
+ "resource:///modules/backup/CookiesBackupResource.sys.mjs"
+);
+
+const { FormHistoryBackupResource } = ChromeUtils.importESModule(
+ "resource:///modules/backup/FormHistoryBackupResource.sys.mjs"
);
-const { TelemetryTestUtils } = ChromeUtils.importESModule(
- "resource://testing-common/TelemetryTestUtils.sys.mjs"
+const { SessionStoreBackupResource } = ChromeUtils.importESModule(
+ "resource:///modules/backup/SessionStoreBackupResource.sys.mjs"
);
add_setup(() => {
- do_get_profile();
// FOG needs to be initialized in order for data to flow.
Services.fog.initializeFOG();
Services.telemetry.clearScalars();
});
/**
+ * Tests that calling `BackupService.takeMeasurements` will call the measure
+ * method of all registered BackupResource classes.
+ */
+add_task(async function test_takeMeasurements() {
+ let sandbox = sinon.createSandbox();
+ sandbox.stub(FakeBackupResource1.prototype, "measure").resolves();
+ sandbox
+ .stub(FakeBackupResource2.prototype, "measure")
+ .rejects(new Error("Some failure to measure"));
+
+ let bs = new BackupService({ FakeBackupResource1, FakeBackupResource2 });
+ await bs.takeMeasurements();
+
+ for (let backupResourceClass of [FakeBackupResource1, FakeBackupResource2]) {
+ Assert.ok(
+ backupResourceClass.prototype.measure.calledOnce,
+ "Measure was called"
+ );
+ Assert.ok(
+ backupResourceClass.prototype.measure.calledWith(PathUtils.profileDir),
+ "Measure was called with the profile directory argument"
+ );
+ }
+
+ sandbox.restore();
+});
+
+/**
* Tests that we can measure the disk space available in the profile directory.
*/
add_task(async function test_profDDiskSpace() {
@@ -38,3 +75,503 @@ add_task(async function test_profDDiskSpace() {
"device"
);
});
+
+/**
+ * Tests that we can measure credentials related files in the profile directory.
+ */
+add_task(async function test_credentialsAndSecurityBackupResource() {
+ Services.fog.testResetFOG();
+
+ const EXPECTED_CREDENTIALS_KILOBYTES_SIZE = 413;
+ const EXPECTED_SECURITY_KILOBYTES_SIZE = 231;
+
+ // Create resource files in temporary directory
+ const tempDir = await IOUtils.createUniqueDirectory(
+ PathUtils.tempDir,
+ "CredentialsAndSecurityBackupResource-measurement-test"
+ );
+
+ const mockFiles = [
+ // Set up credentials files
+ { path: "key4.db", sizeInKB: 300 },
+ { path: "logins.json", sizeInKB: 1 },
+ { path: "logins-backup.json", sizeInKB: 1 },
+ { path: "autofill-profiles.json", sizeInKB: 1 },
+ { path: "credentialstate.sqlite", sizeInKB: 100 },
+ { path: "signedInUser.json", sizeInKB: 5 },
+ // Set up security files
+ { path: "cert9.db", sizeInKB: 230 },
+ { path: "pkcs11.txt", sizeInKB: 1 },
+ ];
+
+ await createTestFiles(tempDir, mockFiles);
+
+ let credentialsAndSecurityBackupResource =
+ new CredentialsAndSecurityBackupResource();
+ await credentialsAndSecurityBackupResource.measure(tempDir);
+
+ let credentialsMeasurement =
+ Glean.browserBackup.credentialsDataSize.testGetValue();
+ let securityMeasurement = Glean.browserBackup.securityDataSize.testGetValue();
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
+
+ // Credentials measurements
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.credentials_data_size",
+ credentialsMeasurement,
+ "Glean and telemetry measurements for credentials data should be equal"
+ );
+
+ Assert.equal(
+ credentialsMeasurement,
+ EXPECTED_CREDENTIALS_KILOBYTES_SIZE,
+ "Should have collected the correct glean measurement for credentials files"
+ );
+
+ // Security measurements
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.security_data_size",
+ securityMeasurement,
+ "Glean and telemetry measurements for security data should be equal"
+ );
+ Assert.equal(
+ securityMeasurement,
+ EXPECTED_SECURITY_KILOBYTES_SIZE,
+ "Should have collected the correct glean measurement for security files"
+ );
+
+ // Cleanup
+ await maybeRemovePath(tempDir);
+});
+
+/**
+ * Tests that we can measure the Cookies db in a profile directory.
+ */
+add_task(async function test_cookiesBackupResource() {
+ const EXPECTED_COOKIES_DB_SIZE = 1230;
+
+ Services.fog.testResetFOG();
+
+ // Create resource files in temporary directory
+ let tempDir = PathUtils.tempDir;
+ let tempCookiesDBPath = PathUtils.join(tempDir, "cookies.sqlite");
+ await createKilobyteSizedFile(tempCookiesDBPath, EXPECTED_COOKIES_DB_SIZE);
+
+ let cookiesBackupResource = new CookiesBackupResource();
+ await cookiesBackupResource.measure(tempDir);
+
+ let cookiesMeasurement = Glean.browserBackup.cookiesSize.testGetValue();
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
+
+ // Compare glean vs telemetry measurements
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.cookies_size",
+ cookiesMeasurement,
+ "Glean and telemetry measurements for cookies.sqlite should be equal"
+ );
+
+ // Compare glean measurements vs actual file sizes
+ Assert.equal(
+ cookiesMeasurement,
+ EXPECTED_COOKIES_DB_SIZE,
+ "Should have collected the correct glean measurement for cookies.sqlite"
+ );
+
+ await maybeRemovePath(tempCookiesDBPath);
+});
+
+/**
+ * Tests that we can measure the Form History db in a profile directory.
+ */
+add_task(async function test_formHistoryBackupResource() {
+ const EXPECTED_FORM_HISTORY_DB_SIZE = 500;
+
+ Services.fog.testResetFOG();
+
+ // Create resource files in temporary directory
+ let tempDir = PathUtils.tempDir;
+ let tempFormHistoryDBPath = PathUtils.join(tempDir, "formhistory.sqlite");
+ await createKilobyteSizedFile(
+ tempFormHistoryDBPath,
+ EXPECTED_FORM_HISTORY_DB_SIZE
+ );
+
+ let formHistoryBackupResource = new FormHistoryBackupResource();
+ await formHistoryBackupResource.measure(tempDir);
+
+ let formHistoryMeasurement =
+ Glean.browserBackup.formHistorySize.testGetValue();
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
+
+ // Compare glean vs telemetry measurements
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.form_history_size",
+ formHistoryMeasurement,
+ "Glean and telemetry measurements for formhistory.sqlite should be equal"
+ );
+
+ // Compare glean measurements vs actual file sizes
+ Assert.equal(
+ formHistoryMeasurement,
+ EXPECTED_FORM_HISTORY_DB_SIZE,
+ "Should have collected the correct glean measurement for formhistory.sqlite"
+ );
+
+ await IOUtils.remove(tempFormHistoryDBPath);
+});
+
+/**
+ * Tests that we can measure the Session Store JSON and backups directory.
+ */
+add_task(async function test_sessionStoreBackupResource() {
+ const EXPECTED_KILOBYTES_FOR_BACKUPS_DIR = 1000;
+ Services.fog.testResetFOG();
+
+ // Create the sessionstore-backups directory.
+ let tempDir = PathUtils.tempDir;
+ let sessionStoreBackupsPath = PathUtils.join(
+ tempDir,
+ "sessionstore-backups",
+ "restore.jsonlz4"
+ );
+ await createKilobyteSizedFile(
+ sessionStoreBackupsPath,
+ EXPECTED_KILOBYTES_FOR_BACKUPS_DIR
+ );
+
+ let sessionStoreBackupResource = new SessionStoreBackupResource();
+ await sessionStoreBackupResource.measure(tempDir);
+
+ let sessionStoreBackupsDirectoryMeasurement =
+ Glean.browserBackup.sessionStoreBackupsDirectorySize.testGetValue();
+ let sessionStoreMeasurement =
+ Glean.browserBackup.sessionStoreSize.testGetValue();
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
+
+ // Compare glean vs telemetry measurements
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.session_store_backups_directory_size",
+ sessionStoreBackupsDirectoryMeasurement,
+ "Glean and telemetry measurements for session store backups directory should be equal"
+ );
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.session_store_size",
+ sessionStoreMeasurement,
+ "Glean and telemetry measurements for session store should be equal"
+ );
+
+ // Compare glean measurements vs actual file sizes
+ Assert.equal(
+ sessionStoreBackupsDirectoryMeasurement,
+ EXPECTED_KILOBYTES_FOR_BACKUPS_DIR,
+ "Should have collected the correct glean measurement for the sessionstore-backups directory"
+ );
+
+ // Session store measurement is from `getCurrentState`, so exact size is unknown.
+ Assert.greater(
+ sessionStoreMeasurement,
+ 0,
+ "Should have collected a measurement for the session store"
+ );
+
+ await IOUtils.remove(sessionStoreBackupsPath);
+});
+
+/**
+ * Tests that we can measure the size of all the addons & extensions data.
+ */
+add_task(async function test_AddonsBackupResource() {
+ Services.fog.testResetFOG();
+ Services.telemetry.clearScalars();
+
+ const EXPECTED_KILOBYTES_FOR_EXTENSIONS_JSON = 250;
+ const EXPECTED_KILOBYTES_FOR_EXTENSIONS_STORE = 500;
+ const EXPECTED_KILOBYTES_FOR_STORAGE_SYNC = 50;
+ const EXPECTED_KILOBYTES_FOR_EXTENSIONS_XPI_A = 600;
+ const EXPECTED_KILOBYTES_FOR_EXTENSIONS_XPI_B = 400;
+ const EXPECTED_KILOBYTES_FOR_EXTENSIONS_XPI_C = 150;
+ const EXPECTED_KILOBYTES_FOR_EXTENSIONS_DIRECTORY = 1000;
+ const EXPECTED_KILOBYTES_FOR_EXTENSION_DATA = 100;
+ const EXPECTED_KILOBYTES_FOR_EXTENSIONS_STORAGE = 200;
+
+ let tempDir = PathUtils.tempDir;
+
+ // Create extensions json files (all the same size).
+ const extensionsFilePath = PathUtils.join(tempDir, "extensions.json");
+ await createKilobyteSizedFile(
+ extensionsFilePath,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_JSON
+ );
+ const extensionSettingsFilePath = PathUtils.join(
+ tempDir,
+ "extension-settings.json"
+ );
+ await createKilobyteSizedFile(
+ extensionSettingsFilePath,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_JSON
+ );
+ const extensionsPrefsFilePath = PathUtils.join(
+ tempDir,
+ "extension-preferences.json"
+ );
+ await createKilobyteSizedFile(
+ extensionsPrefsFilePath,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_JSON
+ );
+ const addonStartupFilePath = PathUtils.join(tempDir, "addonStartup.json.lz4");
+ await createKilobyteSizedFile(
+ addonStartupFilePath,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_JSON
+ );
+
+ // Create the extension store permissions data file.
+ let extensionStorePermissionsDataSize = PathUtils.join(
+ tempDir,
+ "extension-store-permissions",
+ "data.safe.bin"
+ );
+ await createKilobyteSizedFile(
+ extensionStorePermissionsDataSize,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_STORE
+ );
+
+ // Create the storage sync database file.
+ let storageSyncPath = PathUtils.join(tempDir, "storage-sync-v2.sqlite");
+ await createKilobyteSizedFile(
+ storageSyncPath,
+ EXPECTED_KILOBYTES_FOR_STORAGE_SYNC
+ );
+
+ // Create the extensions directory with XPI files.
+ let extensionsXpiAPath = PathUtils.join(
+ tempDir,
+ "extensions",
+ "extension-b.xpi"
+ );
+ let extensionsXpiBPath = PathUtils.join(
+ tempDir,
+ "extensions",
+ "extension-a.xpi"
+ );
+ await createKilobyteSizedFile(
+ extensionsXpiAPath,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_XPI_A
+ );
+ await createKilobyteSizedFile(
+ extensionsXpiBPath,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_XPI_B
+ );
+ // Should be ignored.
+ let extensionsXpiStagedPath = PathUtils.join(
+ tempDir,
+ "extensions",
+ "staged",
+ "staged-test-extension.xpi"
+ );
+ let extensionsXpiTrashPath = PathUtils.join(
+ tempDir,
+ "extensions",
+ "trash",
+ "trashed-test-extension.xpi"
+ );
+ let extensionsXpiUnpackedPath = PathUtils.join(
+ tempDir,
+ "extensions",
+ "unpacked-extension.xpi",
+ "manifest.json"
+ );
+ await createKilobyteSizedFile(
+ extensionsXpiStagedPath,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_XPI_C
+ );
+ await createKilobyteSizedFile(
+ extensionsXpiTrashPath,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_XPI_C
+ );
+ await createKilobyteSizedFile(
+ extensionsXpiUnpackedPath,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_XPI_C
+ );
+
+ // Create the browser extension data directory.
+ let browserExtensionDataPath = PathUtils.join(
+ tempDir,
+ "browser-extension-data",
+ "test-file"
+ );
+ await createKilobyteSizedFile(
+ browserExtensionDataPath,
+ EXPECTED_KILOBYTES_FOR_EXTENSION_DATA
+ );
+
+ // Create the extensions storage directory.
+ let extensionsStoragePath = PathUtils.join(
+ tempDir,
+ "storage",
+ "default",
+ "moz-extension+++test-extension-id",
+ "idb",
+ "data.sqlite"
+ );
+ // Other storage files that should not be counted.
+ let otherStoragePath = PathUtils.join(
+ tempDir,
+ "storage",
+ "default",
+ "https+++accounts.firefox.com",
+ "ls",
+ "data.sqlite"
+ );
+
+ await createKilobyteSizedFile(
+ extensionsStoragePath,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_STORAGE
+ );
+ await createKilobyteSizedFile(
+ otherStoragePath,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_STORAGE
+ );
+
+ // Measure all the extensions data.
+ let extensionsBackupResource = new AddonsBackupResource();
+ await extensionsBackupResource.measure(tempDir);
+
+ let extensionsJsonSizeMeasurement =
+ Glean.browserBackup.extensionsJsonSize.testGetValue();
+ Assert.equal(
+ extensionsJsonSizeMeasurement,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_JSON * 4, // There are 4 equally sized files.
+ "Should have collected the correct measurement of the total size of all extensions JSON files"
+ );
+
+ let extensionStorePermissionsDataSizeMeasurement =
+ Glean.browserBackup.extensionStorePermissionsDataSize.testGetValue();
+ Assert.equal(
+ extensionStorePermissionsDataSizeMeasurement,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_STORE,
+ "Should have collected the correct measurement of the size of the extension store permissions data"
+ );
+
+ let storageSyncSizeMeasurement =
+ Glean.browserBackup.storageSyncSize.testGetValue();
+ Assert.equal(
+ storageSyncSizeMeasurement,
+ EXPECTED_KILOBYTES_FOR_STORAGE_SYNC,
+ "Should have collected the correct measurement of the size of the storage sync database"
+ );
+
+ let extensionsXpiDirectorySizeMeasurement =
+ Glean.browserBackup.extensionsXpiDirectorySize.testGetValue();
+ Assert.equal(
+ extensionsXpiDirectorySizeMeasurement,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_DIRECTORY,
+ "Should have collected the correct measurement of the size 2 equally sized XPI files in the extensions directory"
+ );
+
+ let browserExtensionDataSizeMeasurement =
+ Glean.browserBackup.browserExtensionDataSize.testGetValue();
+ Assert.equal(
+ browserExtensionDataSizeMeasurement,
+ EXPECTED_KILOBYTES_FOR_EXTENSION_DATA,
+ "Should have collected the correct measurement of the size of the browser extension data directory"
+ );
+
+ let extensionsStorageSizeMeasurement =
+ Glean.browserBackup.extensionsStorageSize.testGetValue();
+ Assert.equal(
+ extensionsStorageSizeMeasurement,
+ EXPECTED_KILOBYTES_FOR_EXTENSIONS_STORAGE,
+ "Should have collected the correct measurement of all the extensions storage"
+ );
+
+ // Compare glean vs telemetry measurements
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.extensions_json_size",
+ extensionsJsonSizeMeasurement,
+ "Glean and telemetry measurements for extensions JSON should be equal"
+ );
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.extension_store_permissions_data_size",
+ extensionStorePermissionsDataSizeMeasurement,
+ "Glean and telemetry measurements for extension store permissions data should be equal"
+ );
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.storage_sync_size",
+ storageSyncSizeMeasurement,
+ "Glean and telemetry measurements for storage sync database should be equal"
+ );
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.extensions_xpi_directory_size",
+ extensionsXpiDirectorySizeMeasurement,
+ "Glean and telemetry measurements for extensions directory should be equal"
+ );
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.browser_extension_data_size",
+ browserExtensionDataSizeMeasurement,
+ "Glean and telemetry measurements for browser extension data should be equal"
+ );
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "browser.backup.extensions_storage_size",
+ extensionsStorageSizeMeasurement,
+ "Glean and telemetry measurements for extensions storage should be equal"
+ );
+
+ await maybeRemovePath(tempDir);
+});
+
+/**
+ * Tests that we can handle the extension store permissions data not existing.
+ */
+add_task(
+ async function test_AddonsBackupResource_no_extension_store_permissions_data() {
+ Services.fog.testResetFOG();
+
+ let tempDir = PathUtils.tempDir;
+
+ let extensionsBackupResource = new AddonsBackupResource();
+ await extensionsBackupResource.measure(tempDir);
+
+ let extensionStorePermissionsDataSizeMeasurement =
+ Glean.browserBackup.extensionStorePermissionsDataSize.testGetValue();
+ Assert.equal(
+ extensionStorePermissionsDataSizeMeasurement,
+ null,
+ "Should NOT have collected a measurement for the missing data"
+ );
+ }
+);
+
+/**
+ * Tests that we can handle a profile with no moz-extension IndexedDB databases.
+ */
+add_task(
+ async function test_AddonsBackupResource_no_extension_storage_databases() {
+ Services.fog.testResetFOG();
+
+ let tempDir = PathUtils.tempDir;
+
+ let extensionsBackupResource = new AddonsBackupResource();
+ await extensionsBackupResource.measure(tempDir);
+
+ let extensionsStorageSizeMeasurement =
+ Glean.browserBackup.extensionsStorageSize.testGetValue();
+ Assert.equal(
+ extensionsStorageSizeMeasurement,
+ null,
+ "Should NOT have collected a measurement for the missing data"
+ );
+ }
+);
diff --git a/browser/components/backup/tests/xpcshell/xpcshell.toml b/browser/components/backup/tests/xpcshell/xpcshell.toml
index fb6dcd6846..07e517f1f2 100644
--- a/browser/components/backup/tests/xpcshell/xpcshell.toml
+++ b/browser/components/backup/tests/xpcshell/xpcshell.toml
@@ -1,8 +1,20 @@
[DEFAULT]
+head = "head.js"
firefox-appdir = "browser"
skip-if = ["os == 'android'"]
+prefs = [
+ "browser.backup.log=true",
+]
-["test_BrowserResource.js"]
+["test_BackupResource.js"]
support-files = ["data/test_xulstore.json"]
+["test_MiscDataBackupResource.js"]
+
+["test_PlacesBackupResource.js"]
+
+["test_PreferencesBackupResource.js"]
+
+["test_createBackup.js"]
+
["test_measurements.js"]