From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../migration/tests/unit/test_fx_telemetry.js | 393 +++++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 browser/components/migration/tests/unit/test_fx_telemetry.js (limited to 'browser/components/migration/tests/unit/test_fx_telemetry.js') diff --git a/browser/components/migration/tests/unit/test_fx_telemetry.js b/browser/components/migration/tests/unit/test_fx_telemetry.js new file mode 100644 index 0000000000..2660998588 --- /dev/null +++ b/browser/components/migration/tests/unit/test_fx_telemetry.js @@ -0,0 +1,393 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; +const { FirefoxProfileMigrator } = ChromeUtils.importESModule( + "resource:///modules/FirefoxProfileMigrator.sys.mjs" +); +const { InternalTestingProfileMigrator } = ChromeUtils.importESModule( + "resource:///modules/InternalTestingProfileMigrator.sys.mjs" +); + +// These preferences are set to true anytime MigratorBase.migrate +// successfully completes a migration of their type. +const BOOKMARKS_PREF = "browser.migrate.interactions.bookmarks"; +const HISTORY_PREF = "browser.migrate.interactions.history"; +const PASSWORDS_PREF = "browser.migrate.interactions.passwords"; + +function readFile(file) { + let stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance( + Ci.nsIFileInputStream + ); + stream.init(file, -1, -1, Ci.nsIFileInputStream.CLOSE_ON_EOF); + + let sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance( + Ci.nsIScriptableInputStream + ); + sis.init(stream); + let contents = sis.read(file.fileSize); + sis.close(); + return contents; +} + +function checkDirectoryContains(dir, files) { + print("checking " + dir.path + " - should contain " + Object.keys(files)); + let seen = new Set(); + for (let file of dir.directoryEntries) { + print("found file: " + file.path); + Assert.ok(file.leafName in files, file.leafName + " exists, but shouldn't"); + + let expectedContents = files[file.leafName]; + if (typeof expectedContents != "string") { + // it's a subdir - recurse! + Assert.ok(file.isDirectory(), "should be a subdir"); + let newDir = dir.clone(); + newDir.append(file.leafName); + checkDirectoryContains(newDir, expectedContents); + } else { + Assert.ok(!file.isDirectory(), "should be a regular file"); + let contents = readFile(file); + Assert.equal(contents, expectedContents); + } + seen.add(file.leafName); + } + let missing = []; + for (let x in files) { + if (!seen.has(x)) { + missing.push(x); + } + } + Assert.deepEqual(missing, [], "no missing files in " + dir.path); +} + +function getTestDirs() { + // we make a directory structure in a temp dir which mirrors what we are + // testing. + let tempDir = do_get_tempdir(); + let srcDir = tempDir.clone(); + srcDir.append("test_source_dir"); + srcDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + + let targetDir = tempDir.clone(); + targetDir.append("test_target_dir"); + targetDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + + // no need to cleanup these dirs - the xpcshell harness will do it for us. + return [srcDir, targetDir]; +} + +function writeToFile(dir, leafName, contents) { + let file = dir.clone(); + file.append(leafName); + + let outputStream = FileUtils.openFileOutputStream(file); + outputStream.write(contents, contents.length); + outputStream.close(); +} + +function createSubDir(dir, subDirName) { + let subDir = dir.clone(); + subDir.append(subDirName); + subDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + return subDir; +} + +async function promiseMigrator(name, srcDir, targetDir) { + // As the FirefoxProfileMigrator is a startup-only migrator, we import its + // module and instantiate it directly rather than going through MigrationUtils, + // to bypass that availability check. + let migrator = new FirefoxProfileMigrator(); + let migrators = migrator._getResourcesInternal(srcDir, targetDir); + for (let m of migrators) { + if (m.name == name) { + return new Promise(resolve => m.migrate(resolve)); + } + } + throw new Error("failed to find the " + name + " migrator"); +} + +function promiseTelemetryMigrator(srcDir, targetDir) { + return promiseMigrator("telemetry", srcDir, targetDir); +} + +add_task(async function test_empty() { + let [srcDir, targetDir] = getTestDirs(); + let ok = await promiseTelemetryMigrator(srcDir, targetDir); + Assert.ok(ok, "callback should have been true with empty directories"); + // check both are empty + checkDirectoryContains(srcDir, {}); + checkDirectoryContains(targetDir, {}); +}); + +add_task(async function test_migrate_files() { + let [srcDir, targetDir] = getTestDirs(); + + // Set up datareporting files, some to copy, some not. + let stateContent = JSON.stringify({ + clientId: "68d5474e-19dc-45c1-8e9a-81fca592707c", + }); + let sessionStateContent = "foobar 5432"; + let subDir = createSubDir(srcDir, "datareporting"); + writeToFile(subDir, "state.json", stateContent); + writeToFile(subDir, "session-state.json", sessionStateContent); + writeToFile(subDir, "other.file", "do not copy"); + + let archived = createSubDir(subDir, "archived"); + writeToFile(archived, "other.file", "do not copy"); + + // Set up FHR files, they should not be copied. + writeToFile(srcDir, "healthreport.sqlite", "do not copy"); + writeToFile(srcDir, "healthreport.sqlite-wal", "do not copy"); + subDir = createSubDir(srcDir, "healthreport"); + writeToFile(subDir, "state.json", "do not copy"); + writeToFile(subDir, "other.file", "do not copy"); + + // Perform migration. + let ok = await promiseTelemetryMigrator(srcDir, targetDir); + Assert.ok( + ok, + "callback should have been true with important telemetry files copied" + ); + + checkDirectoryContains(targetDir, { + datareporting: { + "state.json": stateContent, + "session-state.json": sessionStateContent, + }, + }); +}); + +add_task(async function test_datareporting_not_dir() { + let [srcDir, targetDir] = getTestDirs(); + + writeToFile(srcDir, "datareporting", "I'm a file but should be a directory"); + + let ok = await promiseTelemetryMigrator(srcDir, targetDir); + Assert.ok( + ok, + "callback should have been true even though the directory was a file" + ); + + checkDirectoryContains(targetDir, {}); +}); + +add_task(async function test_datareporting_empty() { + let [srcDir, targetDir] = getTestDirs(); + + // Migrate with an empty 'datareporting' subdir. + createSubDir(srcDir, "datareporting"); + let ok = await promiseTelemetryMigrator(srcDir, targetDir); + Assert.ok(ok, "callback should have been true"); + + // We should end up with no migrated files. + checkDirectoryContains(targetDir, { + datareporting: {}, + }); +}); + +add_task(async function test_healthreport_empty() { + let [srcDir, targetDir] = getTestDirs(); + + // Migrate with no 'datareporting' and an empty 'healthreport' subdir. + createSubDir(srcDir, "healthreport"); + let ok = await promiseTelemetryMigrator(srcDir, targetDir); + Assert.ok(ok, "callback should have been true"); + + // We should end up with no migrated files. + checkDirectoryContains(targetDir, {}); +}); + +add_task(async function test_datareporting_many() { + let [srcDir, targetDir] = getTestDirs(); + + // Create some datareporting files. + let subDir = createSubDir(srcDir, "datareporting"); + let shouldBeCopied = "should be copied"; + writeToFile(subDir, "state.json", shouldBeCopied); + writeToFile(subDir, "session-state.json", shouldBeCopied); + writeToFile(subDir, "something.else", "should not"); + createSubDir(subDir, "emptyDir"); + + let ok = await promiseTelemetryMigrator(srcDir, targetDir); + Assert.ok(ok, "callback should have been true"); + + checkDirectoryContains(targetDir, { + datareporting: { + "state.json": shouldBeCopied, + "session-state.json": shouldBeCopied, + }, + }); +}); + +add_task(async function test_no_session_state() { + let [srcDir, targetDir] = getTestDirs(); + + // Check that migration still works properly if we only have state.json. + let subDir = createSubDir(srcDir, "datareporting"); + let stateContent = "abcd984"; + writeToFile(subDir, "state.json", stateContent); + + let ok = await promiseTelemetryMigrator(srcDir, targetDir); + Assert.ok(ok, "callback should have been true"); + + checkDirectoryContains(targetDir, { + datareporting: { + "state.json": stateContent, + }, + }); +}); + +add_task(async function test_no_state() { + let [srcDir, targetDir] = getTestDirs(); + + // Check that migration still works properly if we only have session-state.json. + let subDir = createSubDir(srcDir, "datareporting"); + let sessionStateContent = "abcd512"; + writeToFile(subDir, "session-state.json", sessionStateContent); + + let ok = await promiseTelemetryMigrator(srcDir, targetDir); + Assert.ok(ok, "callback should have been true"); + + checkDirectoryContains(targetDir, { + datareporting: { + "session-state.json": sessionStateContent, + }, + }); +}); + +add_task(async function test_times_migration() { + let [srcDir, targetDir] = getTestDirs(); + + // create a times.json in the source directory. + let contents = JSON.stringify({ created: 1234 }); + writeToFile(srcDir, "times.json", contents); + + let earliest = Date.now(); + let ok = await promiseMigrator("times", srcDir, targetDir); + Assert.ok(ok, "callback should have been true"); + let latest = Date.now(); + + let timesFile = targetDir.clone(); + timesFile.append("times.json"); + + let raw = readFile(timesFile); + let times = JSON.parse(raw); + Assert.ok(times.reset >= earliest && times.reset <= latest); + // and it should have left the creation time alone. + Assert.equal(times.created, 1234); +}); + +/** + * Tests that when importing bookmarks, history, or passwords, we + * set interaction prefs. These preferences are sent using + * TelemetryEnvironment.sys.mjs. + */ +add_task(async function test_interaction_telemetry() { + let testingMigrator = await MigrationUtils.getMigrator( + InternalTestingProfileMigrator.key + ); + + Services.prefs.clearUserPref(BOOKMARKS_PREF); + Services.prefs.clearUserPref(HISTORY_PREF); + Services.prefs.clearUserPref(PASSWORDS_PREF); + + // Ensure that these prefs start false. + Assert.ok(!Services.prefs.getBoolPref(BOOKMARKS_PREF)); + Assert.ok(!Services.prefs.getBoolPref(HISTORY_PREF)); + Assert.ok(!Services.prefs.getBoolPref(PASSWORDS_PREF)); + + await testingMigrator.migrate( + MigrationUtils.resourceTypes.BOOKMARKS, + false, + InternalTestingProfileMigrator.testProfile + ); + Assert.ok( + Services.prefs.getBoolPref(BOOKMARKS_PREF), + "Bookmarks pref should have been set." + ); + Assert.ok(!Services.prefs.getBoolPref(HISTORY_PREF)); + Assert.ok(!Services.prefs.getBoolPref(PASSWORDS_PREF)); + + await testingMigrator.migrate( + MigrationUtils.resourceTypes.HISTORY, + false, + InternalTestingProfileMigrator.testProfile + ); + Assert.ok( + Services.prefs.getBoolPref(BOOKMARKS_PREF), + "Bookmarks pref should have been set." + ); + Assert.ok( + Services.prefs.getBoolPref(HISTORY_PREF), + "History pref should have been set." + ); + Assert.ok(!Services.prefs.getBoolPref(PASSWORDS_PREF)); + + await testingMigrator.migrate( + MigrationUtils.resourceTypes.PASSWORDS, + false, + InternalTestingProfileMigrator.testProfile + ); + Assert.ok( + Services.prefs.getBoolPref(BOOKMARKS_PREF), + "Bookmarks pref should have been set." + ); + Assert.ok( + Services.prefs.getBoolPref(HISTORY_PREF), + "History pref should have been set." + ); + Assert.ok( + Services.prefs.getBoolPref(PASSWORDS_PREF), + "Passwords pref should have been set." + ); + + // Now make sure that we still record these if we migrate a + // series of resources at the same time. + Services.prefs.clearUserPref(BOOKMARKS_PREF); + Services.prefs.clearUserPref(HISTORY_PREF); + Services.prefs.clearUserPref(PASSWORDS_PREF); + + await testingMigrator.migrate( + MigrationUtils.resourceTypes.ALL, + false, + InternalTestingProfileMigrator.testProfile + ); + Assert.ok( + Services.prefs.getBoolPref(BOOKMARKS_PREF), + "Bookmarks pref should have been set." + ); + Assert.ok( + Services.prefs.getBoolPref(HISTORY_PREF), + "History pref should have been set." + ); + Assert.ok( + Services.prefs.getBoolPref(PASSWORDS_PREF), + "Passwords pref should have been set." + ); +}); + +/** + * Tests that interaction preferences used for TelemetryEnvironment are + * persisted across profile resets. + */ +add_task(async function test_interaction_telemetry_persist_across_reset() { + const PREFS = ` +user_pref("${BOOKMARKS_PREF}", true); +user_pref("${HISTORY_PREF}", true); +user_pref("${PASSWORDS_PREF}", true); + `; + + let [srcDir, targetDir] = getTestDirs(); + writeToFile(srcDir, "prefs.js", PREFS); + + let ok = await promiseTelemetryMigrator(srcDir, targetDir); + Assert.ok(ok, "callback should have been true"); + + let prefsPath = PathUtils.join(targetDir.path, "prefs.js"); + Assert.ok(await IOUtils.exists(prefsPath), "Prefs should have been written."); + let writtenPrefsString = await IOUtils.readUTF8(prefsPath); + for (let prefKey of [BOOKMARKS_PREF, HISTORY_PREF, PASSWORDS_PREF]) { + const EXPECTED = `user_pref("${prefKey}", true);`; + Assert.ok(writtenPrefsString.includes(EXPECTED), "Found persisted pref."); + } +}); -- cgit v1.2.3