From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- toolkit/profile/xpcshell/head.js | 623 +++++++++++++++++++++ toolkit/profile/xpcshell/test_check_backup.js | 56 ++ toolkit/profile/xpcshell/test_claim_locked.js | 68 +++ toolkit/profile/xpcshell/test_clean.js | 165 ++++++ toolkit/profile/xpcshell/test_conflict_installs.js | 40 ++ toolkit/profile/xpcshell/test_conflict_profiles.js | 57 ++ toolkit/profile/xpcshell/test_create_default.js | 52 ++ .../profile/xpcshell/test_fix_directory_case.js | 113 ++++ .../xpcshell/test_ignore_legacy_directory.js | 129 +++++ .../profile/xpcshell/test_invalid_descriptor.js | 55 ++ toolkit/profile/xpcshell/test_legacy_empty.js | 28 + toolkit/profile/xpcshell/test_legacy_select.js | 67 +++ toolkit/profile/xpcshell/test_lock.js | 72 +++ .../profile/xpcshell/test_missing_profilesini.js | 67 +++ toolkit/profile/xpcshell/test_new_default.js | 129 +++++ .../profile/xpcshell/test_previous_dedicated.js | 62 ++ toolkit/profile/xpcshell/test_profile_reset.js | 59 ++ toolkit/profile/xpcshell/test_remove.js | 103 ++++ toolkit/profile/xpcshell/test_remove_default.js | 79 +++ .../test_select_backgroundtasks_ephemeral.js | 30 + ..._select_backgroundtasks_not_ephemeral_create.js | 78 +++ ..._select_backgroundtasks_not_ephemeral_exists.js | 66 +++ toolkit/profile/xpcshell/test_select_default.js | 71 +++ .../profile/xpcshell/test_select_environment.js | 45 ++ .../xpcshell/test_select_environment_named.js | 61 ++ toolkit/profile/xpcshell/test_select_missing.js | 34 ++ toolkit/profile/xpcshell/test_select_named.js | 43 ++ toolkit/profile/xpcshell/test_select_noname.js | 35 ++ .../xpcshell/test_select_profile_argument.js | 50 ++ .../xpcshell/test_select_profile_argument_new.js | 43 ++ .../profile/xpcshell/test_select_profilemanager.js | 34 ++ .../xpcshell/test_single_profile_selected.js | 73 +++ .../xpcshell/test_single_profile_unselected.js | 74 +++ .../xpcshell/test_skip_locked_environment.js | 131 +++++ toolkit/profile/xpcshell/test_snap.js | 65 +++ toolkit/profile/xpcshell/test_snap_empty.js | 26 + .../profile/xpcshell/test_snatch_environment.js | 101 ++++ .../xpcshell/test_snatch_environment_default.js | 101 ++++ toolkit/profile/xpcshell/test_startswithlast.js | 28 + toolkit/profile/xpcshell/test_steal_inuse.js | 77 +++ .../xpcshell/test_update_selected_dedicated.js | 80 +++ .../xpcshell/test_update_unknown_dedicated.js | 85 +++ .../xpcshell/test_update_unselected_dedicated.js | 89 +++ toolkit/profile/xpcshell/test_use_dedicated.js | 107 ++++ toolkit/profile/xpcshell/xpcshell.toml | 97 ++++ 45 files changed, 3748 insertions(+) create mode 100644 toolkit/profile/xpcshell/head.js create mode 100644 toolkit/profile/xpcshell/test_check_backup.js create mode 100644 toolkit/profile/xpcshell/test_claim_locked.js create mode 100644 toolkit/profile/xpcshell/test_clean.js create mode 100644 toolkit/profile/xpcshell/test_conflict_installs.js create mode 100644 toolkit/profile/xpcshell/test_conflict_profiles.js create mode 100644 toolkit/profile/xpcshell/test_create_default.js create mode 100644 toolkit/profile/xpcshell/test_fix_directory_case.js create mode 100644 toolkit/profile/xpcshell/test_ignore_legacy_directory.js create mode 100644 toolkit/profile/xpcshell/test_invalid_descriptor.js create mode 100644 toolkit/profile/xpcshell/test_legacy_empty.js create mode 100644 toolkit/profile/xpcshell/test_legacy_select.js create mode 100644 toolkit/profile/xpcshell/test_lock.js create mode 100644 toolkit/profile/xpcshell/test_missing_profilesini.js create mode 100644 toolkit/profile/xpcshell/test_new_default.js create mode 100644 toolkit/profile/xpcshell/test_previous_dedicated.js create mode 100644 toolkit/profile/xpcshell/test_profile_reset.js create mode 100644 toolkit/profile/xpcshell/test_remove.js create mode 100644 toolkit/profile/xpcshell/test_remove_default.js create mode 100644 toolkit/profile/xpcshell/test_select_backgroundtasks_ephemeral.js create mode 100644 toolkit/profile/xpcshell/test_select_backgroundtasks_not_ephemeral_create.js create mode 100644 toolkit/profile/xpcshell/test_select_backgroundtasks_not_ephemeral_exists.js create mode 100644 toolkit/profile/xpcshell/test_select_default.js create mode 100644 toolkit/profile/xpcshell/test_select_environment.js create mode 100644 toolkit/profile/xpcshell/test_select_environment_named.js create mode 100644 toolkit/profile/xpcshell/test_select_missing.js create mode 100644 toolkit/profile/xpcshell/test_select_named.js create mode 100644 toolkit/profile/xpcshell/test_select_noname.js create mode 100644 toolkit/profile/xpcshell/test_select_profile_argument.js create mode 100644 toolkit/profile/xpcshell/test_select_profile_argument_new.js create mode 100644 toolkit/profile/xpcshell/test_select_profilemanager.js create mode 100644 toolkit/profile/xpcshell/test_single_profile_selected.js create mode 100644 toolkit/profile/xpcshell/test_single_profile_unselected.js create mode 100644 toolkit/profile/xpcshell/test_skip_locked_environment.js create mode 100644 toolkit/profile/xpcshell/test_snap.js create mode 100644 toolkit/profile/xpcshell/test_snap_empty.js create mode 100644 toolkit/profile/xpcshell/test_snatch_environment.js create mode 100644 toolkit/profile/xpcshell/test_snatch_environment_default.js create mode 100644 toolkit/profile/xpcshell/test_startswithlast.js create mode 100644 toolkit/profile/xpcshell/test_steal_inuse.js create mode 100644 toolkit/profile/xpcshell/test_update_selected_dedicated.js create mode 100644 toolkit/profile/xpcshell/test_update_unknown_dedicated.js create mode 100644 toolkit/profile/xpcshell/test_update_unselected_dedicated.js create mode 100644 toolkit/profile/xpcshell/test_use_dedicated.js create mode 100644 toolkit/profile/xpcshell/xpcshell.toml (limited to 'toolkit/profile/xpcshell') diff --git a/toolkit/profile/xpcshell/head.js b/toolkit/profile/xpcshell/head.js new file mode 100644 index 0000000000..dc354f5b62 --- /dev/null +++ b/toolkit/profile/xpcshell/head.js @@ -0,0 +1,623 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { NetUtil } = ChromeUtils.importESModule( + "resource://gre/modules/NetUtil.sys.mjs" +); +const { FileUtils } = ChromeUtils.importESModule( + "resource://gre/modules/FileUtils.sys.mjs" +); +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); +const { TelemetryTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TelemetryTestUtils.sys.mjs" +); + +const NS_ERROR_START_PROFILE_MANAGER = 0x805800c9; + +const UPDATE_CHANNEL = AppConstants.MOZ_UPDATE_CHANNEL; + +let gProfD = do_get_profile(); +let gDataHome = gProfD.clone(); +gDataHome.append("data"); +gDataHome.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755); +let gDataHomeLocal = gProfD.clone(); +gDataHomeLocal.append("local"); +gDataHomeLocal.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755); + +let xreDirProvider = Cc["@mozilla.org/xre/directory-provider;1"].getService( + Ci.nsIXREDirProvider +); +xreDirProvider.setUserDataDirectory(gDataHome, false); +xreDirProvider.setUserDataDirectory(gDataHomeLocal, true); + +let gIsDefaultApp = false; + +const ShellService = { + register() { + let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + + let factory = { + createInstance(iid) { + return ShellService.QueryInterface(iid); + }, + }; + + registrar.registerFactory( + this.ID, + "ToolkitShellService", + this.CONTRACT, + factory + ); + }, + + isDefaultApplication() { + return gIsDefaultApp; + }, + + QueryInterface: ChromeUtils.generateQI(["nsIToolkitShellService"]), + ID: Components.ID("{ce724e0c-ed70-41c9-ab31-1033b0b591be}"), + CONTRACT: "@mozilla.org/toolkit/shell-service;1", +}; + +ShellService.register(); + +let gIsLegacy = false; + +function enableLegacyProfiles() { + Services.env.set("MOZ_LEGACY_PROFILES", "1"); + + gIsLegacy = true; +} + +function getProfileService() { + return Cc["@mozilla.org/toolkit/profile-service;1"].getService( + Ci.nsIToolkitProfileService + ); +} + +let PROFILE_DEFAULT = "default"; +let DEDICATED_NAME = `default-${UPDATE_CHANNEL}`; +if (AppConstants.MOZ_DEV_EDITION) { + DEDICATED_NAME = PROFILE_DEFAULT = "dev-edition-default"; +} + +// Shared data for backgroundtasks tests. +const BACKGROUNDTASKS_PROFILE_DATA = (() => { + let hash = xreDirProvider.getInstallHash(); + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: "Profile1", + path: "Path1", + default: false, + }, + { + name: "Profile3", + path: "Path3", + default: false, + }, + ], + installs: { + [hash]: { + default: "Path1", + }, + }, + backgroundTasksProfiles: [ + { + name: `MozillaBackgroundTask-${hash}-unrelated_task`, + path: `saltsalt.MozillaBackgroundTask-${hash}-unrelated_task`, + }, + ], + }; + return profileData; +})(); + +/** + * Creates a random profile path for use. + */ +function makeRandomProfileDir(name) { + let file = gDataHome.clone(); + file.append(name); + file.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755); + return file; +} + +/** + * A wrapper around nsIToolkitProfileService.selectStartupProfile to make it + * a bit nicer to use from JS. + */ +function selectStartupProfile(args = [], isResetting = false, legacyHash = "") { + let service = getProfileService(); + let rootDir = {}; + let localDir = {}; + let profile = {}; + let didCreate = service.selectStartupProfile( + ["xpcshell", ...args], + isResetting, + UPDATE_CHANNEL, + legacyHash, + rootDir, + localDir, + profile + ); + + if (profile.value) { + Assert.ok( + rootDir.value.equals(profile.value.rootDir), + "Should have matched the root dir." + ); + Assert.ok( + localDir.value.equals(profile.value.localDir), + "Should have matched the local dir." + ); + Assert.ok( + service.currentProfile === profile.value, + "Should have marked the profile as the current profile." + ); + } else { + Assert.ok(!service.currentProfile, "Should be no current profile."); + } + + return { + rootDir: rootDir.value, + localDir: localDir.value, + profile: profile.value, + didCreate, + }; +} + +function testStartsProfileManager(args = [], isResetting = false) { + try { + selectStartupProfile(args, isResetting); + Assert.ok(false, "Should have started the profile manager"); + checkStartupReason(); + } catch (e) { + Assert.equal( + e.result, + NS_ERROR_START_PROFILE_MANAGER, + "Should have started the profile manager" + ); + } +} + +function safeGet(ini, section, key) { + try { + return ini.getString(section, key); + } catch (e) { + return null; + } +} + +/** + * Writes a compatibility.ini file that marks the give profile directory as last + * used by the given install path. + */ +function writeCompatibilityIni( + dir, + appDir = FileUtils.getDir("CurProcD", []), + greDir = FileUtils.getDir("GreD", []) +) { + let target = dir.clone(); + target.append("compatibility.ini"); + + let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].getService( + Ci.nsIINIParserFactory + ); + let ini = factory.createINIParser().QueryInterface(Ci.nsIINIParserWriter); + + // The profile service doesn't care about these so just use fixed values + ini.setString( + "Compatibility", + "LastVersion", + "64.0a1_20180919123806/20180919123806" + ); + ini.setString("Compatibility", "LastOSABI", "Darwin_x86_64-gcc3"); + + ini.setString( + "Compatibility", + "LastPlatformDir", + greDir.persistentDescriptor + ); + ini.setString("Compatibility", "LastAppDir", appDir.persistentDescriptor); + + ini.writeFile(target); +} + +/** + * Writes a profiles.ini based on the passed profile data. + * profileData should contain two properties, options and profiles. + * options contains a single property, startWithLastProfile. + * profiles is an array of profiles each containing name, path and default + * properties. + */ +function writeProfilesIni(profileData) { + let target = gDataHome.clone(); + target.append("profiles.ini"); + + let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].getService( + Ci.nsIINIParserFactory + ); + let ini = factory.createINIParser().QueryInterface(Ci.nsIINIParserWriter); + + const { + options = {}, + profiles = [], + installs = null, + backgroundTasksProfiles = null, + } = profileData; + + let { startWithLastProfile = true } = options; + ini.setString( + "General", + "StartWithLastProfile", + startWithLastProfile ? "1" : "0" + ); + + for (let i = 0; i < profiles.length; i++) { + let profile = profiles[i]; + let section = `Profile${i}`; + + ini.setString(section, "Name", profile.name); + ini.setString(section, "IsRelative", 1); + ini.setString(section, "Path", profile.path); + + if (profile.default) { + ini.setString(section, "Default", "1"); + } + } + + if (backgroundTasksProfiles) { + let section = "BackgroundTasksProfiles"; + for (let backgroundTasksProfile of backgroundTasksProfiles) { + ini.setString( + section, + backgroundTasksProfile.name, + backgroundTasksProfile.path + ); + } + } + + if (installs) { + ini.setString("General", "Version", "2"); + + for (let hash of Object.keys(installs)) { + ini.setString(`Install${hash}`, "Default", installs[hash].default); + if ("locked" in installs[hash]) { + ini.setString( + `Install${hash}`, + "Locked", + installs[hash].locked ? "1" : "0" + ); + } + } + + writeInstallsIni({ installs }); + } else { + writeInstallsIni(null); + } + + ini.writeFile(target); +} + +/** + * Reads the existing profiles.ini into the same structure as that accepted by + * writeProfilesIni above. The profiles property is sorted according to name + * because the order is irrelevant and it makes testing easier if we can make + * that assumption. + */ +function readProfilesIni() { + let target = gDataHome.clone(); + target.append("profiles.ini"); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [], + installs: null, + }; + + if (!target.exists()) { + return profileData; + } + + let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].getService( + Ci.nsIINIParserFactory + ); + let ini = factory.createINIParser(target); + + profileData.options.startWithLastProfile = + safeGet(ini, "General", "StartWithLastProfile") == "1"; + if (safeGet(ini, "General", "Version") == "2") { + profileData.installs = {}; + } + + let sections = ini.getSections(); + while (sections.hasMore()) { + let section = sections.getNext(); + + if (section == "General") { + continue; + } + + if (section.startsWith("Profile")) { + let isRelative = safeGet(ini, section, "IsRelative"); + if (isRelative === null) { + break; + } + Assert.equal( + isRelative, + "1", + "Paths should always be relative in these tests." + ); + + let profile = { + name: safeGet(ini, section, "Name"), + path: safeGet(ini, section, "Path"), + }; + + try { + profile.default = ini.getString(section, "Default") == "1"; + Assert.ok( + profile.default, + "The Default value is only written when true." + ); + } catch (e) { + profile.default = false; + } + + profileData.profiles.push(profile); + } + + if (section.startsWith("Install")) { + Assert.ok( + profileData.installs, + "Should only see an install section if the ini version was correct." + ); + + profileData.installs[section.substring(7)] = { + default: safeGet(ini, section, "Default"), + }; + + let locked = safeGet(ini, section, "Locked"); + if (locked !== null) { + profileData.installs[section.substring(7)].locked = locked; + } + } + + if (section == "BackgroundTasksProfiles") { + profileData.backgroundTasksProfiles = []; + let backgroundTasksProfiles = ini.getKeys(section); + while (backgroundTasksProfiles.hasMore()) { + let name = backgroundTasksProfiles.getNext(); + let path = ini.getString(section, name); + profileData.backgroundTasksProfiles.push({ name, path }); + } + profileData.backgroundTasksProfiles.sort((a, b) => + a.name.localeCompare(b.name) + ); + } + } + + profileData.profiles.sort((a, b) => a.name.localeCompare(b.name)); + + return profileData; +} + +/** + * Writes an installs.ini based on the supplied data. Should be an object with + * keys for every installation hash each mapping to an object. Each object + * should have a default property for the relative path to the profile. + */ +function writeInstallsIni(installData) { + let target = gDataHome.clone(); + target.append("installs.ini"); + + if (!installData) { + try { + target.remove(false); + } catch (e) {} + return; + } + + const { installs = {} } = installData; + + let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].getService( + Ci.nsIINIParserFactory + ); + let ini = factory.createINIParser(null).QueryInterface(Ci.nsIINIParserWriter); + + for (let hash of Object.keys(installs)) { + ini.setString(hash, "Default", installs[hash].default); + if ("locked" in installs[hash]) { + ini.setString(hash, "Locked", installs[hash].locked ? "1" : "0"); + } + } + + ini.writeFile(target); +} + +/** + * Reads installs.ini into a structure like that used in the above function. + */ +function readInstallsIni() { + let target = gDataHome.clone(); + target.append("installs.ini"); + + let installData = { + installs: {}, + }; + + if (!target.exists()) { + return installData; + } + + let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].getService( + Ci.nsIINIParserFactory + ); + let ini = factory.createINIParser(target); + + let sections = ini.getSections(); + while (sections.hasMore()) { + let hash = sections.getNext(); + if (hash != "General") { + installData.installs[hash] = { + default: safeGet(ini, hash, "Default"), + }; + + let locked = safeGet(ini, hash, "Locked"); + if (locked !== null) { + installData.installs[hash].locked = locked; + } + } + } + + return installData; +} + +/** + * Check that the backup data in installs.ini matches the install data in + * profiles.ini. + */ +function checkBackup( + profileData = readProfilesIni(), + installData = readInstallsIni() +) { + if (!profileData.installs) { + // If the profiles db isn't of the right version we wouldn't expect the + // backup to be accurate. + return; + } + + Assert.deepEqual( + profileData.installs, + installData.installs, + "Backup installs.ini should match installs in profiles.ini" + ); +} + +/** + * Checks that the profile service seems to have the right data in it compared + * to profile and install data structured as in the above functions. + */ +function checkProfileService( + profileData = readProfilesIni(), + verifyBackup = true +) { + let service = getProfileService(); + + let expectedStartWithLast = true; + if ("options" in profileData) { + expectedStartWithLast = profileData.options.startWithLastProfile; + } + + Assert.equal( + service.startWithLastProfile, + expectedStartWithLast, + "Start with last profile should match." + ); + + let serviceProfiles = Array.from(service.profiles); + + Assert.equal( + serviceProfiles.length, + profileData.profiles.length, + "Should be the same number of profiles." + ); + + // Sort to make matching easy. + serviceProfiles.sort((a, b) => a.name.localeCompare(b.name)); + profileData.profiles.sort((a, b) => a.name.localeCompare(b.name)); + + let hash = xreDirProvider.getInstallHash(); + let defaultPath = + profileData.installs && hash in profileData.installs + ? profileData.installs[hash].default + : null; + let dedicatedProfile = null; + let legacyProfile = null; + + for (let i = 0; i < serviceProfiles.length; i++) { + let serviceProfile = serviceProfiles[i]; + let expectedProfile = profileData.profiles[i]; + + Assert.equal( + serviceProfile.name, + expectedProfile.name, + "Should have the same name." + ); + + let expectedPath = Cc["@mozilla.org/file/local;1"].createInstance( + Ci.nsIFile + ); + expectedPath.setRelativeDescriptor(gDataHome, expectedProfile.path); + Assert.equal( + serviceProfile.rootDir.path, + expectedPath.path, + "Should have the same path." + ); + + if (expectedProfile.path == defaultPath) { + dedicatedProfile = serviceProfile; + } + + if (AppConstants.MOZ_DEV_EDITION) { + if (expectedProfile.name == PROFILE_DEFAULT) { + legacyProfile = serviceProfile; + } + } else if (expectedProfile.default) { + legacyProfile = serviceProfile; + } + } + + if (gIsLegacy || Services.env.get("SNAP_NAME")) { + Assert.equal( + service.defaultProfile, + legacyProfile, + "Should have seen the right profile selected." + ); + } else { + Assert.equal( + service.defaultProfile, + dedicatedProfile, + "Should have seen the right profile selected." + ); + } + + if (verifyBackup) { + checkBackup(profileData); + } +} + +// Maps the interesting scalar IDs to simple names that can be used as JS variables. +const SCALARS = { + selectionReason: "startup.profile_selection_reason", + databaseVersion: "startup.profile_database_version", + profileCount: "startup.profile_count", +}; + +function getTelemetryScalars() { + let scalars = TelemetryTestUtils.getProcessScalars("parent"); + + let results = {}; + for (let [prop, scalarId] of Object.entries(SCALARS)) { + results[prop] = scalars[scalarId]; + } + + return results; +} + +function checkStartupReason(expected = undefined) { + let { selectionReason } = getTelemetryScalars(); + + Assert.equal( + selectionReason, + expected, + "Should have seen the right startup reason." + ); +} diff --git a/toolkit/profile/xpcshell/test_check_backup.js b/toolkit/profile/xpcshell/test_check_backup.js new file mode 100644 index 0000000000..2f5a9dbf18 --- /dev/null +++ b/toolkit/profile/xpcshell/test_check_backup.js @@ -0,0 +1,56 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that when the profiles DB is missing the install data we reload it. + */ + +add_task(async () => { + let hash = xreDirProvider.getInstallHash(); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: "Profile1", + path: "Path1", + }, + { + name: "Profile2", + path: "Path2", + }, + ], + }; + + let installs = { + [hash]: { + default: "Path2", + }, + }; + + writeProfilesIni(profileData); + writeInstallsIni({ installs }); + + let { profile, didCreate } = selectStartupProfile(); + checkStartupReason("default"); + let { databaseVersion, profileCount } = getTelemetryScalars(); + Assert.equal( + databaseVersion, + "1", + "Old database file was present at startup." + ); + Assert.equal(profileCount, 2, "Should be two profiles."); + + // Should have added the backup data to the service, check that is true. + profileData.installs = installs; + checkProfileService(profileData); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.equal( + profile.name, + "Profile2", + "Should have selected the right profile" + ); +}); diff --git a/toolkit/profile/xpcshell/test_claim_locked.js b/toolkit/profile/xpcshell/test_claim_locked.js new file mode 100644 index 0000000000..bffdea39dc --- /dev/null +++ b/toolkit/profile/xpcshell/test_claim_locked.js @@ -0,0 +1,68 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that an old-style default profile already locked to a different install + * isn't claimed by this install. + */ + +add_task(async () => { + let defaultProfile = makeRandomProfileDir("default"); + + writeCompatibilityIni(defaultProfile); + + writeProfilesIni({ + profiles: [ + { + name: "Foo", + path: defaultProfile.leafName, + default: true, + }, + ], + installs: { + other: { + default: defaultProfile.leafName, + locked: true, + }, + }, + }); + + let { profile: selectedProfile, didCreate } = selectStartupProfile(); + + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 2, + "Should have the right number of profiles." + ); + + let hash = xreDirProvider.getInstallHash(); + Assert.equal( + Object.keys(profileData.installs).length, + 2, + "Should be two known installs." + ); + Assert.notEqual( + profileData.installs[hash].default, + defaultProfile.leafName, + "Should not have marked the original default profile as the default for this install." + ); + Assert.ok( + profileData.installs[hash].locked, + "Should have locked as we created this profile for this install." + ); + + checkProfileService(profileData); + + Assert.ok(didCreate, "Should have created a new profile."); + Assert.ok( + !selectedProfile.rootDir.equals(defaultProfile), + "Should be using a different directory." + ); + Assert.equal(selectedProfile.name, DEDICATED_NAME); +}); diff --git a/toolkit/profile/xpcshell/test_clean.js b/toolkit/profile/xpcshell/test_clean.js new file mode 100644 index 0000000000..3132d5457d --- /dev/null +++ b/toolkit/profile/xpcshell/test_clean.js @@ -0,0 +1,165 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests from a clean state. + * Then does some testing that creating new profiles and marking them as + * selected works. + */ + +add_task(async () => { + let service = getProfileService(); + + let target = gDataHome.clone(); + target.append("profiles.ini"); + Assert.ok(!target.exists(), "profiles.ini should not exist yet."); + target.leafName = "installs.ini"; + Assert.ok(!target.exists(), "installs.ini should not exist yet."); + + // Create a new profile to use. + let newProfile = service.createProfile(null, "dedicated"); + service.flush(); + + let profileData = readProfilesIni(); + + Assert.equal( + profileData.profiles.length, + 1, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, "dedicated", "Should have the right name."); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + // The new profile hasn't been marked as the default yet! + Assert.equal( + Object.keys(profileData.installs).length, + 0, + "Should be no defaults for installs yet." + ); + + checkProfileService(profileData); + + Assert.ok( + service.startWithLastProfile, + "Should be set to start with the last profile." + ); + service.startWithLastProfile = false; + Assert.ok( + !service.startWithLastProfile, + "Should be set to not start with the last profile." + ); + + service.defaultProfile = newProfile; + service.flush(); + + profileData = readProfilesIni(); + + Assert.equal( + profileData.profiles.length, + 1, + "Should have the right number of profiles." + ); + + profile = profileData.profiles[0]; + Assert.equal(profile.name, "dedicated", "Should have the right name."); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + let hash = xreDirProvider.getInstallHash(); + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should be only one known install." + ); + Assert.equal( + profileData.installs[hash].default, + profileData.profiles[0].path, + "Should have marked the new profile as the default for this install." + ); + + checkProfileService(profileData); + + let otherProfile = service.createProfile(null, "another"); + service.defaultProfile = otherProfile; + + service.flush(); + + profileData = readProfilesIni(); + + Assert.equal( + profileData.profiles.length, + 2, + "Should have the right number of profiles." + ); + + profile = profileData.profiles[0]; + Assert.equal(profile.name, "another", "Should have the right name."); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + profile = profileData.profiles[1]; + Assert.equal(profile.name, "dedicated", "Should have the right name."); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should be only one known install." + ); + Assert.equal( + profileData.installs[hash].default, + profileData.profiles[0].path, + "Should have marked the new profile as the default for this install." + ); + + checkProfileService(profileData); + + newProfile.remove(true); + service.flush(); + + profileData = readProfilesIni(); + + Assert.equal( + profileData.profiles.length, + 1, + "Should have the right number of profiles." + ); + + profile = profileData.profiles[0]; + Assert.equal(profile.name, "another", "Should have the right name."); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should be only one known install." + ); + Assert.equal( + profileData.installs[hash].default, + profileData.profiles[0].path, + "Should have marked the new profile as the default for this install." + ); + + checkProfileService(profileData); + + otherProfile.remove(true); + service.flush(); + + profileData = readProfilesIni(); + + Assert.equal( + profileData.profiles.length, + 0, + "Should have the right number of profiles." + ); + + // We leave a reference to the missing profile to stop us trying to steal the + // old-style default profile on next startup. + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should be only one known install." + ); + + checkProfileService(profileData); +}); diff --git a/toolkit/profile/xpcshell/test_conflict_installs.js b/toolkit/profile/xpcshell/test_conflict_installs.js new file mode 100644 index 0000000000..741e7ed70f --- /dev/null +++ b/toolkit/profile/xpcshell/test_conflict_installs.js @@ -0,0 +1,40 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that the profile service refuses to flush when the install.ini file + * has been modified. + */ + +function check_unchanged(service) { + Assert.ok( + !service.isListOutdated, + "Should not have detected a modification." + ); + try { + service.flush(); + Assert.ok(true, "Should have flushed."); + } catch (e) { + Assert.ok(false, "Should have succeeded flushing."); + } +} + +add_task(async () => { + let service = getProfileService(); + + Assert.ok(!service.isListOutdated, "Should not be modified yet."); + + let installsini = gDataHome.clone(); + installsini.append("installs.ini"); + + Assert.ok(!installsini.exists(), "File should not exist yet."); + installsini.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644); + + installsini.remove(false); + // We have to do profile selection to actually have any install data. + selectStartupProfile(); + check_unchanged(service); + + // We can't reset the modification time back to exactly what it was, so I + // guess we can't do much more here :( +}); diff --git a/toolkit/profile/xpcshell/test_conflict_profiles.js b/toolkit/profile/xpcshell/test_conflict_profiles.js new file mode 100644 index 0000000000..7028c1d5c2 --- /dev/null +++ b/toolkit/profile/xpcshell/test_conflict_profiles.js @@ -0,0 +1,57 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that the profile service refuses to flush when the profiles.ini file + * has been modified. + */ + +function check_unchanged(service) { + Assert.ok( + !service.isListOutdated, + "Should not have detected a modification." + ); + try { + service.flush(); + Assert.ok(true, "Should have flushed."); + } catch (e) { + Assert.ok(false, "Should have succeeded flushing."); + } +} + +function check_outdated(service) { + Assert.ok(service.isListOutdated, "Should have detected a modification."); + try { + service.flush(); + Assert.ok(false, "Should have failed to flush."); + } catch (e) { + Assert.equal( + e.result, + Cr.NS_ERROR_DATABASE_CHANGED, + "Should have refused to flush." + ); + } +} + +add_task(async () => { + let service = getProfileService(); + + Assert.ok(!service.isListOutdated, "Should not be modified yet."); + + let profilesini = gDataHome.clone(); + profilesini.append("profiles.ini"); + + Assert.ok(!profilesini.exists(), "File should not exist yet."); + profilesini.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644); + check_outdated(service); + + profilesini.remove(false); + check_unchanged(service); + + let oldTime = profilesini.lastModifiedTime; + profilesini.lastModifiedTime = oldTime - 10000; + check_outdated(service); + + // We can't reset the modification time back to exactly what it was, so I + // guess we can't do much more here :( +}); diff --git a/toolkit/profile/xpcshell/test_create_default.js b/toolkit/profile/xpcshell/test_create_default.js new file mode 100644 index 0000000000..cdac1832bf --- /dev/null +++ b/toolkit/profile/xpcshell/test_create_default.js @@ -0,0 +1,52 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that from an empty database a default profile is created. + */ + +add_task(async () => { + let service = getProfileService(); + let { profile, didCreate } = selectStartupProfile(); + + checkStartupReason("firstrun-created-default"); + + let profileData = readProfilesIni(); + checkProfileService(profileData); + + Assert.ok(didCreate, "Should have created a new profile."); + Assert.equal( + profile, + service.defaultProfile, + "Should now be the default profile." + ); + Assert.equal( + profile.name, + DEDICATED_NAME, + "Should have created a new profile with the right name." + ); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 2, + "Should have the right number of profiles." + ); + + profile = profileData.profiles[0]; + Assert.equal(profile.name, "default", "Should have the right name."); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + profile = profileData.profiles[1]; + Assert.equal(profile.name, DEDICATED_NAME, "Should have the right name."); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + let hash = xreDirProvider.getInstallHash(); + Assert.ok( + profileData.installs[hash].locked, + "Should have locked the profile" + ); +}); diff --git a/toolkit/profile/xpcshell/test_fix_directory_case.js b/toolkit/profile/xpcshell/test_fix_directory_case.js new file mode 100644 index 0000000000..60e3acbe4e --- /dev/null +++ b/toolkit/profile/xpcshell/test_fix_directory_case.js @@ -0,0 +1,113 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests the case where the user has an default profile set for the legacy + * install hash. This should be switched to the new hash and correctly used as + * the default. + */ + +add_task(async () => { + let currentHash = xreDirProvider.getInstallHash(); + let legacyHash = "F87E39E944FE466E"; + + let defaultProfile = makeRandomProfileDir("default"); + let dedicatedProfile = makeRandomProfileDir("dedicated"); + let devProfile = makeRandomProfileDir("devedition"); + + // Make sure we don't steal the old-style default. + writeCompatibilityIni(defaultProfile); + + writeProfilesIni({ + profiles: [ + { + name: "default", + path: defaultProfile.leafName, + default: true, + }, + { + name: "dedicated", + path: dedicatedProfile.leafName, + }, + { + name: "dev-edition-default", + path: devProfile.leafName, + }, + ], + installs: { + [legacyHash]: { + default: dedicatedProfile.leafName, + }, + otherhash: { + default: "foobar", + }, + }, + }); + + let { profile: selectedProfile, didCreate } = selectStartupProfile( + [], + false, + legacyHash + ); + checkStartupReason("default"); + + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 3, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, `dedicated`, "Should have the right name."); + Assert.equal( + profile.path, + dedicatedProfile.leafName, + "Should be the expected dedicated profile." + ); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + profile = profileData.profiles[1]; + Assert.equal(profile.name, "default", "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 3, + "Should be three known installs." + ); + Assert.equal( + profileData.installs[currentHash].default, + dedicatedProfile.leafName, + "Should have switched to the new install hash." + ); + Assert.equal( + profileData.installs[legacyHash].default, + dedicatedProfile.leafName, + "Should have kept the old install hash." + ); + Assert.equal( + profileData.installs.otherhash.default, + "foobar", + "Should have kept the default for the other install." + ); + + checkProfileService(profileData); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok( + selectedProfile.rootDir.equals(dedicatedProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, "dedicated"); +}); diff --git a/toolkit/profile/xpcshell/test_ignore_legacy_directory.js b/toolkit/profile/xpcshell/test_ignore_legacy_directory.js new file mode 100644 index 0000000000..29b1601cc5 --- /dev/null +++ b/toolkit/profile/xpcshell/test_ignore_legacy_directory.js @@ -0,0 +1,129 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests the case where the user has an default profile set for both the legacy + * and new install hash. This should just use the default for the new install + * hash. + */ + +add_task(async () => { + let currentHash = xreDirProvider.getInstallHash(); + let legacyHash = "F87E39E944FE466E"; + + let defaultProfile = makeRandomProfileDir("default"); + let dedicatedProfile = makeRandomProfileDir("dedicated"); + let devProfile = makeRandomProfileDir("devedition"); + + // Make sure we don't steal the old-style default. + writeCompatibilityIni(defaultProfile); + + writeProfilesIni({ + profiles: [ + { + name: "default", + path: defaultProfile.leafName, + default: true, + }, + { + name: "dedicated", + path: dedicatedProfile.leafName, + }, + { + name: "dev-edition-default", + path: devProfile.leafName, + }, + ], + installs: { + [legacyHash]: { + default: defaultProfile.leafName, + }, + [currentHash]: { + default: dedicatedProfile.leafName, + }, + otherhash: { + default: "foobar", + }, + }, + }); + + let { profile: selectedProfile, didCreate } = selectStartupProfile( + [], + false, + legacyHash + ); + checkStartupReason("default"); + + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 3, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, `dedicated`, "Should have the right name."); + Assert.equal( + profile.path, + dedicatedProfile.leafName, + "Should be the expected dedicated profile." + ); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + profile = profileData.profiles[1]; + Assert.equal(profile.name, "default", "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + profile = profileData.profiles[2]; + Assert.equal( + profile.name, + "dev-edition-default", + "Should have the right name." + ); + Assert.equal( + profile.path, + devProfile.leafName, + "Should not be the original default profile." + ); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 3, + "Should be three known installs." + ); + Assert.equal( + profileData.installs[currentHash].default, + dedicatedProfile.leafName, + "Should have switched to the new install hash." + ); + Assert.equal( + profileData.installs[legacyHash].default, + defaultProfile.leafName, + "Should have ignored old install hash." + ); + Assert.equal( + profileData.installs.otherhash.default, + "foobar", + "Should have kept the default for the other install." + ); + + checkProfileService(profileData); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok( + selectedProfile.rootDir.equals(dedicatedProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, "dedicated"); +}); diff --git a/toolkit/profile/xpcshell/test_invalid_descriptor.js b/toolkit/profile/xpcshell/test_invalid_descriptor.js new file mode 100644 index 0000000000..87638d301d --- /dev/null +++ b/toolkit/profile/xpcshell/test_invalid_descriptor.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * If a user has modified a relative profile path then there may be issues where + * the profile default setting doesn't match. + */ + +add_task(async () => { + let hash = xreDirProvider.getInstallHash(); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: "Profile1", + path: "../data/test", + }, + { + name: "Profile2", + path: "Path2", + }, + ], + installs: { + [hash]: { + default: "test", + }, + }, + }; + + writeProfilesIni(profileData); + + let { profile, didCreate } = selectStartupProfile(); + checkStartupReason("default"); + + let service = getProfileService(); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.equal( + profile.name, + "Profile1", + "Should have selected the expected profile" + ); + + Assert.equal( + profile.name, + service.defaultProfile.name, + "Should have selected the right default." + ); + + service.flush(); + checkProfileService(); +}); diff --git a/toolkit/profile/xpcshell/test_legacy_empty.js b/toolkit/profile/xpcshell/test_legacy_empty.js new file mode 100644 index 0000000000..7348006f8b --- /dev/null +++ b/toolkit/profile/xpcshell/test_legacy_empty.js @@ -0,0 +1,28 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that setting MOZ_LEGACY_PROFILES disables dedicated profiles. + */ + +add_task(async () => { + enableLegacyProfiles(); + + let service = getProfileService(); + let { profile, didCreate } = selectStartupProfile(); + checkStartupReason("firstrun-created-default"); + + Assert.ok(didCreate, "Should have created a new profile."); + Assert.equal( + profile.name, + PROFILE_DEFAULT, + "Should have used the normal name." + ); + if (AppConstants.MOZ_DEV_EDITION) { + Assert.equal(service.profileCount, 2, "Should be two profiles."); + } else { + Assert.equal(service.profileCount, 1, "Should be only one profile."); + } + + checkProfileService(); +}); diff --git a/toolkit/profile/xpcshell/test_legacy_select.js b/toolkit/profile/xpcshell/test_legacy_select.js new file mode 100644 index 0000000000..e919169d59 --- /dev/null +++ b/toolkit/profile/xpcshell/test_legacy_select.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that an old-style default profile not previously used by this build + * gets selected when configured for legacy profiles. + */ + +add_task(async () => { + let defaultProfile = makeRandomProfileDir("default"); + + // Just pretend this profile was last used by something in the profile dir. + let greDir = gProfD.clone(); + greDir.append("app"); + writeCompatibilityIni(defaultProfile, greDir, greDir); + + writeProfilesIni({ + profiles: [ + { + name: PROFILE_DEFAULT, + path: defaultProfile.leafName, + default: true, + }, + ], + }); + + enableLegacyProfiles(); + + let { profile: selectedProfile, didCreate } = selectStartupProfile(); + checkStartupReason("default"); + + let profileData = readProfilesIni(); + let installsINI = gDataHome.clone(); + installsINI.append("installs.ini"); + Assert.ok( + !installsINI.exists(), + "Installs database should not have been created." + ); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 1, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, PROFILE_DEFAULT, "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + checkProfileService(profileData); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok( + selectedProfile.rootDir.equals(defaultProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, PROFILE_DEFAULT); +}); diff --git a/toolkit/profile/xpcshell/test_lock.js b/toolkit/profile/xpcshell/test_lock.js new file mode 100644 index 0000000000..7283406e00 --- /dev/null +++ b/toolkit/profile/xpcshell/test_lock.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that when the default application claims the old-style default profile + * it locks it to itself. + */ + +add_task(async () => { + gIsDefaultApp = true; + let defaultProfile = makeRandomProfileDir("default"); + + writeCompatibilityIni(defaultProfile); + + writeProfilesIni({ + profiles: [ + { + name: PROFILE_DEFAULT, + path: defaultProfile.leafName, + default: true, + }, + ], + }); + + let { profile: selectedProfile, didCreate } = selectStartupProfile(); + + let hash = xreDirProvider.getInstallHash(); + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 1, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, PROFILE_DEFAULT, "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should be only one known install." + ); + Assert.equal( + profileData.installs[hash].default, + defaultProfile.leafName, + "Should have marked the original default profile as the default for this install." + ); + Assert.ok( + profileData.installs[hash].locked, + "Should have locked as we're the default app." + ); + + checkProfileService(profileData); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok( + selectedProfile.rootDir.equals(defaultProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, PROFILE_DEFAULT); +}); diff --git a/toolkit/profile/xpcshell/test_missing_profilesini.js b/toolkit/profile/xpcshell/test_missing_profilesini.js new file mode 100644 index 0000000000..74ab7584fa --- /dev/null +++ b/toolkit/profile/xpcshell/test_missing_profilesini.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * When profiles.ini is missing there isn't any point in restoring from any + * installs.ini, the profiles it refers to are gone anyway. + */ + +add_task(async () => { + let hash = xreDirProvider.getInstallHash(); + + let installs = { + [hash]: { + default: "Path2", + }, + otherhash: { + default: "foo", + }, + anotherhash: { + default: "bar", + }, + }; + + writeInstallsIni({ installs }); + + let { profile, didCreate } = selectStartupProfile(); + checkStartupReason("firstrun-created-default"); + + Assert.ok(didCreate, "Should have created a new profile."); + Assert.equal( + profile.name, + DEDICATED_NAME, + "Should have created the right profile" + ); + + let profilesData = readProfilesIni(); + Assert.equal( + Object.keys(profilesData.installs).length, + 1, + "Should be only one known install" + ); + Assert.ok(hash in profilesData.installs, "Should be the expected install."); + Assert.notEqual( + profilesData.installs[hash].default, + "Path2", + "Didn't import the previous data." + ); + Assert.equal( + profilesData.profiles.length, + 2, + "Should be two profiles (old-style default and dedicated default)." + ); + + let { databaseVersion, profileCount } = getTelemetryScalars(); + Assert.equal( + databaseVersion, + "0", + "Database file was not present at startup." + ); + Assert.equal( + profileCount, + 2, + "Should be two profiles (old-style default and dedicated default)." + ); + + checkProfileService(profilesData); +}); diff --git a/toolkit/profile/xpcshell/test_new_default.js b/toolkit/profile/xpcshell/test_new_default.js new file mode 100644 index 0000000000..552433ab95 --- /dev/null +++ b/toolkit/profile/xpcshell/test_new_default.js @@ -0,0 +1,129 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that an old-style default profile previously used by this build gets + * updated to a dedicated profile for this build. + */ + +add_task(async () => { + let mydefaultProfile = makeRandomProfileDir("mydefault"); + let defaultProfile = makeRandomProfileDir("default"); + let devDefaultProfile = makeRandomProfileDir("devedition"); + + writeCompatibilityIni(mydefaultProfile); + writeCompatibilityIni(devDefaultProfile); + + writeProfilesIni({ + profiles: [ + { + name: "mydefault", + path: mydefaultProfile.leafName, + default: true, + }, + { + name: "default", + path: defaultProfile.leafName, + }, + { + name: "dev-edition-default", + path: devDefaultProfile.leafName, + }, + ], + }); + + let { profile: selectedProfile, didCreate } = selectStartupProfile(); + checkStartupReason("firstrun-claimed-default"); + let { databaseVersion, profileCount } = getTelemetryScalars(); + Assert.equal( + databaseVersion, + "1", + "Old database file was present at startup." + ); + Assert.equal(profileCount, 3, "Should be three profiles."); + + let hash = xreDirProvider.getInstallHash(); + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 3, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, "default", "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original non-default profile." + ); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + profile = profileData.profiles[1]; + Assert.equal( + profile.name, + "dev-edition-default", + "Should have the right name." + ); + Assert.equal( + profile.path, + devDefaultProfile.leafName, + "Should be the original dev default profile." + ); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + profile = profileData.profiles[2]; + Assert.equal(profile.name, "mydefault", "Should have the right name."); + Assert.equal( + profile.path, + mydefaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should be only one known install." + ); + if (AppConstants.MOZ_DEV_EDITION) { + Assert.equal( + profileData.installs[hash].default, + devDefaultProfile.leafName, + "Should have marked the original dev default profile as the default for this install." + ); + } else { + Assert.equal( + profileData.installs[hash].default, + mydefaultProfile.leafName, + "Should have marked the original default profile as the default for this install." + ); + } + + Assert.ok( + !profileData.installs[hash].locked, + "Should not be locked as we're not the default app." + ); + + checkProfileService(profileData); + + Assert.ok(!didCreate, "Should not have created a new profile."); + if (AppConstants.MOZ_DEV_EDITION) { + Assert.ok( + selectedProfile.rootDir.equals(devDefaultProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, "dev-edition-default"); + } else { + Assert.ok( + selectedProfile.rootDir.equals(mydefaultProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, "mydefault"); + } +}); diff --git a/toolkit/profile/xpcshell/test_previous_dedicated.js b/toolkit/profile/xpcshell/test_previous_dedicated.js new file mode 100644 index 0000000000..6dd80b4028 --- /dev/null +++ b/toolkit/profile/xpcshell/test_previous_dedicated.js @@ -0,0 +1,62 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * If install.ini lists a default profile for this build but that profile no + * longer exists don't try to steal the old-style default even if it was used + * by this build. It means this install has previously used dedicated profiles. + */ + +add_task(async () => { + let hash = xreDirProvider.getInstallHash(); + let defaultProfile = makeRandomProfileDir("default"); + + writeCompatibilityIni(defaultProfile); + + writeProfilesIni({ + profiles: [ + { + name: "default", + path: defaultProfile.leafName, + default: true, + }, + ], + installs: { + [hash]: { + default: "foobar", + }, + }, + }); + + testStartsProfileManager(); + + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 1, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, "default", "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + // We keep the data here so we don't steal on the next reboot... + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Still list the broken reference." + ); + + checkProfileService(profileData); +}); diff --git a/toolkit/profile/xpcshell/test_profile_reset.js b/toolkit/profile/xpcshell/test_profile_reset.js new file mode 100644 index 0000000000..7cfb5ed3d1 --- /dev/null +++ b/toolkit/profile/xpcshell/test_profile_reset.js @@ -0,0 +1,59 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that from an empty database with profile reset requested a new profile + * is still created. + */ + +add_task(async () => { + let { profile: selectedProfile, didCreate } = selectStartupProfile([], true); + // With no profile we're just create a new profile and skip resetting it. + checkStartupReason("firstrun-created-default"); + checkProfileService(); + + let hash = xreDirProvider.getInstallHash(); + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 2, + "Should have the right number of profiles, ours and the old-style default." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, "default", "Should have the right name."); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + profile = profileData.profiles[1]; + Assert.equal(profile.name, DEDICATED_NAME, "Should have the right name."); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should only be one known installs." + ); + Assert.equal( + profileData.installs[hash].default, + profile.path, + "Should have taken the new profile as the default for the current install." + ); + Assert.ok( + profileData.installs[hash].locked, + "Should have locked as we created this profile." + ); + + checkProfileService(profileData); + + Assert.ok(didCreate, "Should have created a new profile."); + Assert.equal( + selectedProfile.name, + profile.name, + "Should be using the right profile." + ); +}); diff --git a/toolkit/profile/xpcshell/test_remove.js b/toolkit/profile/xpcshell/test_remove.js new file mode 100644 index 0000000000..8b5025d612 --- /dev/null +++ b/toolkit/profile/xpcshell/test_remove.js @@ -0,0 +1,103 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests adding and removing functions correctly. + */ + +function compareLists(service, knownProfiles) { + Assert.equal( + service.profileCount, + knownProfiles.length, + "profileCount should be correct." + ); + let serviceProfiles = Array.from(service.profiles); + Assert.equal( + serviceProfiles.length, + knownProfiles.length, + "Enumerator length should be correct." + ); + + for (let i = 0; i < knownProfiles.length; i++) { + // Cannot use strictEqual here, it attempts to print out a string + // representation of the profile objects and on some platforms that recurses + // infinitely. + Assert.ok( + serviceProfiles[i] === knownProfiles[i], + `Should have the right profile in position ${i}.` + ); + } +} + +function removeProfile(profiles, position) { + dump(`Removing profile in position ${position}.`); + Assert.greaterOrEqual(position, 0, "Should be removing a valid position."); + Assert.less( + position, + profiles.length, + "Should be removing a valid position." + ); + + let last = profiles.pop(); + + if (profiles.length == position) { + // We were asked to remove the last profile. + last.remove(false); + return; + } + + profiles[position].remove(false); + profiles[position] = last; +} + +add_task(async () => { + let service = getProfileService(); + let profiles = []; + compareLists(service, profiles); + + profiles.push(service.createProfile(null, "profile1")); + profiles.push(service.createProfile(null, "profile2")); + profiles.push(service.createProfile(null, "profile3")); + profiles.push(service.createProfile(null, "profile4")); + profiles.push(service.createProfile(null, "profile5")); + profiles.push(service.createProfile(null, "profile6")); + profiles.push(service.createProfile(null, "profile7")); + profiles.push(service.createProfile(null, "profile8")); + profiles.push(service.createProfile(null, "profile9")); + compareLists(service, profiles); + + // Test removing the first profile. + removeProfile(profiles, 0); + compareLists(service, profiles); + + // And the last profile. + removeProfile(profiles, profiles.length - 1); + compareLists(service, profiles); + + // Last but one... + removeProfile(profiles, profiles.length - 2); + compareLists(service, profiles); + + // Second one... + removeProfile(profiles, 1); + compareLists(service, profiles); + + // Something in the middle. + removeProfile(profiles, 2); + compareLists(service, profiles); + + let expectedNames = ["profile9", "profile7", "profile5", "profile4"]; + + let serviceProfiles = Array.from(service.profiles); + for (let i = 0; i < expectedNames.length; i++) { + Assert.equal(serviceProfiles[i].name, expectedNames[i]); + } + + removeProfile(profiles, 0); + removeProfile(profiles, 0); + removeProfile(profiles, 0); + removeProfile(profiles, 0); + + Assert.equal(Array.from(service.profiles).length, 0, "All profiles gone."); + Assert.equal(service.profileCount, 0, "All profiles gone."); +}); diff --git a/toolkit/profile/xpcshell/test_remove_default.js b/toolkit/profile/xpcshell/test_remove_default.js new file mode 100644 index 0000000000..f77f2d87d9 --- /dev/null +++ b/toolkit/profile/xpcshell/test_remove_default.js @@ -0,0 +1,79 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that calling nsIToolkitProfile.remove on the default profile correctly + * removes the profile. + */ + +add_task(async () => { + let hash = xreDirProvider.getInstallHash(); + let defaultProfile = makeRandomProfileDir("default"); + + let profilesIni = { + profiles: [ + { + name: "default", + path: defaultProfile.leafName, + default: true, + }, + ], + installs: { + [hash]: { + default: defaultProfile.leafName, + }, + }, + }; + writeProfilesIni(profilesIni); + + let service = getProfileService(); + checkProfileService(profilesIni); + + let { profile, didCreate } = selectStartupProfile(); + Assert.ok(!didCreate, "Should have not created a new profile."); + Assert.equal( + profile.name, + "default", + "Should have selected the default profile." + ); + Assert.equal( + profile, + service.defaultProfile, + "Should have selected the default profile." + ); + + checkProfileService(profilesIni); + + // In an actual run of Firefox we wouldn't be able to delete the profile in + // use because it would be locked. But we don't actually lock the profile in + // tests. + profile.remove(false); + + Assert.ok(!service.defaultProfile, "Should no longer be a default profile."); + Assert.equal( + profile, + service.currentProfile, + "Should still be the profile in use." + ); + + // These are the modifications that should have been made. + profilesIni.profiles.pop(); + profilesIni.installs[hash].default = ""; + + // The data isn't flushed to disk so don't check the backup here. + checkProfileService(profilesIni, false); + + service.flush(); + + // And that should have flushed to disk correctly. + checkProfileService(); + + // checkProfileService doesn't differentiate between a blank default profile + // for the install and a missing install. + profilesIni = readProfilesIni(); + Assert.equal( + profilesIni.installs[hash].default, + "", + "Should be a blank default profile." + ); +}); diff --git a/toolkit/profile/xpcshell/test_select_backgroundtasks_ephemeral.js b/toolkit/profile/xpcshell/test_select_backgroundtasks_ephemeral.js new file mode 100644 index 0000000000..982df276db --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_backgroundtasks_ephemeral.js @@ -0,0 +1,30 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Verify that background tasks don't touch `profiles.ini` for ephemeral profile + * tasks. + */ + +let condition = { + skip_if: () => !AppConstants.MOZ_BACKGROUNDTASKS, +}; + +add_task(condition, async () => { + writeProfilesIni(BACKGROUNDTASKS_PROFILE_DATA); + + // Pretend that this is a background task. For a task that uses an ephemeral + // profile, `profiles.ini` is untouched. + const bts = Cc["@mozilla.org/backgroundtasks;1"].getService( + Ci.nsIBackgroundTasks + ); + bts.overrideBackgroundTaskNameForTesting("ephemeral_profile"); + + let { didCreate } = selectStartupProfile(); + checkStartupReason("backgroundtask-ephemeral"); + + Assert.equal(didCreate, true, "Created new ephemeral profile"); + + let profileData = readProfilesIni(); + Assert.deepEqual(BACKGROUNDTASKS_PROFILE_DATA, profileData); +}); diff --git a/toolkit/profile/xpcshell/test_select_backgroundtasks_not_ephemeral_create.js b/toolkit/profile/xpcshell/test_select_backgroundtasks_not_ephemeral_create.js new file mode 100644 index 0000000000..ebed889360 --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_backgroundtasks_not_ephemeral_create.js @@ -0,0 +1,78 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Verify that background tasks that create non-ephemeral profiles update + * `profiles.ini` with a salted profile location. + */ + +let condition = { + skip_if: () => !AppConstants.MOZ_BACKGROUNDTASKS, +}; + +// MOZ_APP_VENDOR is empty on Thunderbird. +let vendor = AppConstants.MOZ_APP_NAME == "thunderbird" ? "" : "Mozilla"; + +add_task(condition, async () => { + let hash = xreDirProvider.getInstallHash(); + + writeProfilesIni(BACKGROUNDTASKS_PROFILE_DATA); + + // Pretend that this is a background task. For a task that does *not* use an + // ephemeral profile, i.e., that uses a persistent profile, `profiles.ini` is + // updated with a new entry in section `BackgroundTaskProfiles`. + const bts = Cc["@mozilla.org/backgroundtasks;1"].getService( + Ci.nsIBackgroundTasks + ); + // "not_ephemeral_profile" is a special name recognized by the + // background task system. + bts.overrideBackgroundTaskNameForTesting("not_ephemeral_profile"); + + let { didCreate, rootDir } = selectStartupProfile(); + checkStartupReason("backgroundtask-not-ephemeral"); + + Assert.equal(didCreate, true, "Created new non-ephemeral profile"); + + let profileData = readProfilesIni(); + + // Profile names are lexicographically ordered, and `not_ephemeral_profile` + // sorts before `unrelated_task`. + Assert.equal(profileData.backgroundTasksProfiles.length, 2); + Assert.deepEqual( + [profileData.backgroundTasksProfiles[1]], + BACKGROUNDTASKS_PROFILE_DATA.backgroundTasksProfiles + ); + + let saltedPath = profileData.backgroundTasksProfiles[0].path; + Assert.ok( + saltedPath.endsWith( + `.${vendor}BackgroundTask-${hash}-not_ephemeral_profile` + ), + `${saltedPath} ends with ".${vendor}BackgroundTask-${hash}-not_ephemeral_profile"` + ); + Assert.ok( + !saltedPath.startsWith( + `.${vendor}BackgroundTask-${hash}-not_ephemeral_profile` + ), + `${saltedPath} is really salted` + ); + Assert.deepEqual(profileData.backgroundTasksProfiles[0], { + name: `${vendor}BackgroundTask-${hash}-not_ephemeral_profile`, + path: saltedPath, + }); + + Assert.ok( + rootDir.path.endsWith(saltedPath), + `rootDir "${rootDir.path}" ends with salted path "${saltedPath}"` + ); + + // Really, "UAppData", but this is xpcshell. + let backgroundTasksProfilesPath = gDataHome; + if (!AppConstants.XP_UNIX || AppConstants.platform === "macosx") { + backgroundTasksProfilesPath.append("Background Tasks Profiles"); + } + Assert.ok( + rootDir.path.startsWith(backgroundTasksProfilesPath.path), + `rootDir "${rootDir.path}" is sibling to user profiles directory ${backgroundTasksProfilesPath}` + ); +}); diff --git a/toolkit/profile/xpcshell/test_select_backgroundtasks_not_ephemeral_exists.js b/toolkit/profile/xpcshell/test_select_backgroundtasks_not_ephemeral_exists.js new file mode 100644 index 0000000000..6fa3ec5882 --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_backgroundtasks_not_ephemeral_exists.js @@ -0,0 +1,66 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Verify that background tasks that use non-ephemeral profiles re-use existing + * salted profile locations from `profiles.ini`. + */ + +let condition = { + skip_if: () => !AppConstants.MOZ_BACKGROUNDTASKS, +}; + +// MOZ_APP_VENDOR is empty on Thunderbird. +let vendor = AppConstants.MOZ_APP_NAME == "thunderbird" ? "" : "Mozilla"; + +add_task(condition, async () => { + let hash = xreDirProvider.getInstallHash(); + + let saltedPath = `saltSALT.${vendor}BackgroundTask-${hash}-not_ephemeral_profile`; + + // See note about ordering below. + BACKGROUNDTASKS_PROFILE_DATA.backgroundTasksProfiles.splice(0, 0, { + name: `${vendor}BackgroundTask-${hash}-not_ephemeral_profile`, + path: saltedPath, + }); + + writeProfilesIni(BACKGROUNDTASKS_PROFILE_DATA); + + // Pretend that this is a background task. For a task that does *not* use an + // ephemeral profile, i.e., that uses a persistent profile, when + // `profiles.ini` section `BackgroundTasksProfiles` contains the relevant + // entry, that profile path is re-used. + const bts = Cc["@mozilla.org/backgroundtasks;1"].getService( + Ci.nsIBackgroundTasks + ); + // "not_ephemeral_profile" is a special name recognized by the + // background task system. + bts.overrideBackgroundTaskNameForTesting("not_ephemeral_profile"); + + let { didCreate, rootDir } = selectStartupProfile(); + checkStartupReason("backgroundtask-not-ephemeral"); + + Assert.equal(didCreate, false, "Re-used existing non-ephemeral profile"); + + let profileData = readProfilesIni(); + + // Profile names are lexicographically ordered, and `not_ephemeral_profile` + // sorts before `unrelated_task`. + Assert.equal(profileData.backgroundTasksProfiles.length, 2); + Assert.deepEqual(profileData, BACKGROUNDTASKS_PROFILE_DATA); + + Assert.ok( + rootDir.path.endsWith(saltedPath), + `rootDir "${rootDir.path}" ends with salted path "${saltedPath}"` + ); + + // Really, "UAppData", but this is xpcshell. + let backgroundTasksProfilesPath = gDataHome; + if (!AppConstants.XP_UNIX || AppConstants.platform === "macosx") { + backgroundTasksProfilesPath.append("Background Tasks Profiles"); + } + Assert.ok( + rootDir.path.startsWith(backgroundTasksProfilesPath.path), + `rootDir "${rootDir.path}" is sibling to user profiles directory ${backgroundTasksProfilesPath}` + ); +}); diff --git a/toolkit/profile/xpcshell/test_select_default.js b/toolkit/profile/xpcshell/test_select_default.js new file mode 100644 index 0000000000..df4b27df7e --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_default.js @@ -0,0 +1,71 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that from a database of profiles the default profile is selected. + */ + +add_task(async () => { + let hash = xreDirProvider.getInstallHash(); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: "Profile1", + path: "Path1", + }, + { + name: "Profile3", + path: "Path3", + }, + ], + installs: { + [hash]: { + default: "Path2", + }, + }, + }; + + if (AppConstants.MOZ_DEV_EDITION) { + profileData.profiles.push( + { + name: "default", + path: "Path2", + default: true, + }, + { + name: PROFILE_DEFAULT, + path: "Path4", + } + ); + } else { + profileData.profiles.push({ + name: PROFILE_DEFAULT, + path: "Path2", + default: true, + }); + } + + writeProfilesIni(profileData); + + let { profile, didCreate } = selectStartupProfile(); + checkStartupReason("default"); + + let service = getProfileService(); + checkProfileService(profileData); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.equal( + profile, + service.defaultProfile, + "Should have returned the default profile." + ); + Assert.equal( + profile.name, + "default", + "Should have selected the right profile" + ); +}); diff --git a/toolkit/profile/xpcshell/test_select_environment.js b/toolkit/profile/xpcshell/test_select_environment.js new file mode 100644 index 0000000000..f7e3138191 --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_environment.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that the environment variables are used to select a profile. + */ + +add_task(async () => { + let dir = makeRandomProfileDir("foo"); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: "Profile1", + path: dir.leafName, + }, + { + name: "Profile2", + path: "Path2", + default: true, + }, + { + name: "Profile3", + path: "Path3", + }, + ], + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + Services.env.set("XRE_PROFILE_PATH", dir.path); + Services.env.set("XRE_PROFILE_LOCAL_PATH", dir.path); + + let { rootDir, localDir, profile, didCreate } = selectStartupProfile(); + checkStartupReason("restart"); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok(rootDir.equals(dir), "Should have selected the right root dir."); + Assert.ok(localDir.equals(dir), "Should have selected the right local dir."); + Assert.ok(!profile, "No named profile matches this."); +}); diff --git a/toolkit/profile/xpcshell/test_select_environment_named.js b/toolkit/profile/xpcshell/test_select_environment_named.js new file mode 100644 index 0000000000..348ce0ff0a --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_environment_named.js @@ -0,0 +1,61 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that the environment variables are used to select a profile. + */ + +add_task(async () => { + let root = makeRandomProfileDir("foo"); + let local = gDataHomeLocal.clone(); + local.append("foo"); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: "Profile1", + path: root.leafName, + }, + { + name: "Profile2", + path: "Path2", + default: true, + }, + { + name: "Profile3", + path: "Path3", + }, + ], + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + Services.env.set("XRE_PROFILE_PATH", root.path); + Services.env.set("XRE_PROFILE_LOCAL_PATH", local.path); + + let { rootDir, localDir, profile, didCreate } = selectStartupProfile([ + "-P", + "Profile3", + ]); + checkStartupReason("restart"); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok(rootDir.equals(root), "Should have selected the right root dir."); + Assert.ok( + localDir.equals(local), + "Should have selected the right local dir." + ); + Assert.ok(profile, "A named profile matches this."); + Assert.equal(profile.name, "Profile1", "The right profile was matched."); + + let service = getProfileService(); + Assert.notEqual( + service.defaultProfile, + profile, + "Should not be the default profile." + ); +}); diff --git a/toolkit/profile/xpcshell/test_select_missing.js b/toolkit/profile/xpcshell/test_select_missing.js new file mode 100644 index 0000000000..97da6d76b3 --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_missing.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that when choosing an unknown profile the profile manager is shown. + */ + +add_task(async () => { + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: "Profile1", + path: "Path1", + }, + { + name: "Profile2", + path: "Path2", + default: true, + }, + { + name: "Profile3", + path: "Path3", + }, + ], + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + testStartsProfileManager(["-P", "foo"]); +}); diff --git a/toolkit/profile/xpcshell/test_select_named.js b/toolkit/profile/xpcshell/test_select_named.js new file mode 100644 index 0000000000..abecba1f4d --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_named.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that from a database of profiles the correct profile is selected. + */ + +add_task(async () => { + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: "Profile1", + path: "Path1", + }, + { + name: "Profile2", + path: "Path2", + default: true, + }, + { + name: "Profile3", + path: "Path3", + }, + ], + }; + + writeProfilesIni(profileData); + + checkProfileService(profileData); + + let { profile, didCreate } = selectStartupProfile(["-P", "Profile1"]); + checkStartupReason("argument-p"); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.equal( + profile.name, + "Profile1", + "Should have chosen the right profile" + ); +}); diff --git a/toolkit/profile/xpcshell/test_select_noname.js b/toolkit/profile/xpcshell/test_select_noname.js new file mode 100644 index 0000000000..278b38089c --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_noname.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that when passing the -P command line argument and not passing a + * profile name the profile manager is opened. + */ + +add_task(async () => { + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: "Profile1", + path: "Path1", + }, + { + name: "Profile2", + path: "Path2", + default: true, + }, + { + name: "Profile3", + path: "Path3", + }, + ], + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + testStartsProfileManager(["-P"]); +}); diff --git a/toolkit/profile/xpcshell/test_select_profile_argument.js b/toolkit/profile/xpcshell/test_select_profile_argument.js new file mode 100644 index 0000000000..70be506538 --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_profile_argument.js @@ -0,0 +1,50 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that selecting a profile directory with the "profile" argument finds + * the matching profile. + */ + +add_task(async () => { + let root = makeRandomProfileDir("foo"); + let local = gDataHomeLocal.clone(); + local.append("foo"); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: "Profile1", + path: root.leafName, + }, + ], + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + let { rootDir, localDir, profile, didCreate } = selectStartupProfile([ + "-profile", + root.path, + ]); + checkStartupReason("argument-profile"); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok(rootDir.equals(root), "Should have selected the right root dir."); + Assert.ok( + localDir.equals(local), + "Should have selected the right local dir." + ); + Assert.ok(profile, "A named profile matches this."); + Assert.equal(profile.name, "Profile1", "The right profile was matched."); + + let service = getProfileService(); + Assert.notEqual( + service.defaultProfile, + profile, + "Should not be the default profile." + ); +}); diff --git a/toolkit/profile/xpcshell/test_select_profile_argument_new.js b/toolkit/profile/xpcshell/test_select_profile_argument_new.js new file mode 100644 index 0000000000..570abbd19f --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_profile_argument_new.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that selecting a profile directory with the "profile" argument finds + * doesn't match the incorrect profile. + */ + +add_task(async () => { + let root = makeRandomProfileDir("foo"); + let local = gDataHomeLocal.clone(); + local.append("foo"); + let empty = makeRandomProfileDir("empty"); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: "Profile1", + path: root.leafName, + }, + ], + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + let { rootDir, localDir, profile, didCreate } = selectStartupProfile([ + "-profile", + empty.path, + ]); + checkStartupReason("argument-profile"); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok(rootDir.equals(empty), "Should have selected the right root dir."); + Assert.ok( + localDir.equals(empty), + "Should have selected the right local dir." + ); + Assert.ok(!profile, "No named profile matches this."); +}); diff --git a/toolkit/profile/xpcshell/test_select_profilemanager.js b/toolkit/profile/xpcshell/test_select_profilemanager.js new file mode 100644 index 0000000000..7c8bdd820e --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_profilemanager.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that when requested the profile manager is shown. + */ + +add_task(async () => { + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: "Profile1", + path: "Path1", + }, + { + name: "Profile2", + path: "Path2", + default: true, + }, + { + name: "Profile3", + path: "Path3", + }, + ], + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + testStartsProfileManager(["-profilemanager"]); +}); diff --git a/toolkit/profile/xpcshell/test_single_profile_selected.js b/toolkit/profile/xpcshell/test_single_profile_selected.js new file mode 100644 index 0000000000..1dc1f5c872 --- /dev/null +++ b/toolkit/profile/xpcshell/test_single_profile_selected.js @@ -0,0 +1,73 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Previous versions of Firefox automatically used a single profile even if it + * wasn't marked as the default. So we should try to upgrade that one if it was + * last used by this build. This test checks the case where it was. + */ + +add_task(async () => { + let defaultProfile = makeRandomProfileDir("default"); + + writeCompatibilityIni(defaultProfile); + + writeProfilesIni({ + profiles: [ + { + name: "default", + path: defaultProfile.leafName, + default: false, + }, + ], + }); + + let { profile: selectedProfile, didCreate } = selectStartupProfile(); + checkStartupReason("firstrun-claimed-default"); + + let hash = xreDirProvider.getInstallHash(); + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 1, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, "default", "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should be only one known install." + ); + Assert.equal( + profileData.installs[hash].default, + defaultProfile.leafName, + "Should have marked the original default profile as the default for this install." + ); + Assert.ok( + !profileData.installs[hash].locked, + "Should not have locked as we're not the default app." + ); + + checkProfileService(profileData); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok( + selectedProfile.rootDir.equals(defaultProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, "default"); +}); diff --git a/toolkit/profile/xpcshell/test_single_profile_unselected.js b/toolkit/profile/xpcshell/test_single_profile_unselected.js new file mode 100644 index 0000000000..3ad36de387 --- /dev/null +++ b/toolkit/profile/xpcshell/test_single_profile_unselected.js @@ -0,0 +1,74 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Previous versions of Firefox automatically used a single profile even if it + * wasn't marked as the default. So we should try to upgrade that one if it was + * last used by this build. This test checks the case where it wasn't. + */ + +add_task(async () => { + let defaultProfile = makeRandomProfileDir("default"); + + // Just pretend this profile was last used by something in the profile dir. + let greDir = gProfD.clone(); + greDir.append("app"); + writeCompatibilityIni(defaultProfile, greDir, greDir); + + writeProfilesIni({ + profiles: [ + { + name: "default", + path: defaultProfile.leafName, + default: false, + }, + ], + }); + + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 1, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, "default", "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + Assert.ok(!profileData.installs, "Should be no defaults for installs yet."); + + checkProfileService(profileData); + + let { profile: selectedProfile, didCreate } = selectStartupProfile(); + checkStartupReason("firstrun-skipped-default"); + Assert.ok(didCreate, "Should have created a new profile."); + Assert.ok( + !selectedProfile.rootDir.equals(defaultProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, DEDICATED_NAME); + + profileData = readProfilesIni(); + + profile = profileData.profiles[0]; + Assert.equal(profile.name, "default", "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should now be marked as the old-style default."); + + checkProfileService(profileData); +}); diff --git a/toolkit/profile/xpcshell/test_skip_locked_environment.js b/toolkit/profile/xpcshell/test_skip_locked_environment.js new file mode 100644 index 0000000000..f98f6dd88f --- /dev/null +++ b/toolkit/profile/xpcshell/test_skip_locked_environment.js @@ -0,0 +1,131 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that the environment variables are used to select a profile and that + * on the first run of a dedicated profile build we don't snatch it if it is + * locked by another install. + */ + +add_task(async () => { + let root = makeRandomProfileDir("foo"); + let local = gDataHomeLocal.clone(); + local.append("foo"); + + writeCompatibilityIni(root); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: PROFILE_DEFAULT, + path: root.leafName, + default: true, + }, + { + name: "Profile2", + path: "Path2", + }, + { + name: "Profile3", + path: "Path3", + }, + ], + // Another install is using the profile and it is locked. + installs: { + otherinstall: { + default: root.leafName, + locked: true, + }, + }, + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + Services.env.set("XRE_PROFILE_PATH", root.path); + Services.env.set("XRE_PROFILE_LOCAL_PATH", local.path); + + let { rootDir, localDir, profile, didCreate } = selectStartupProfile(); + checkStartupReason("restart-skipped-default"); + + // Since there is already a profile with the desired name on dev-edition, a + // unique version will be used. + let expectedName = AppConstants.MOZ_DEV_EDITION + ? `${DEDICATED_NAME}-1` + : DEDICATED_NAME; + + Assert.ok(didCreate, "Should have created a new profile."); + Assert.ok(!rootDir.equals(root), "Should have selected the right root dir."); + Assert.ok( + !localDir.equals(local), + "Should have selected the right local dir." + ); + Assert.ok(profile, "A named profile was returned."); + Assert.equal(profile.name, expectedName, "The right profile name was used."); + + let service = getProfileService(); + Assert.equal( + service.defaultProfile, + profile, + "Should be the default profile." + ); + Assert.equal( + service.currentProfile, + profile, + "Should be the current profile." + ); + + profileData = readProfilesIni(); + + Assert.equal( + profileData.profiles[0].name, + PROFILE_DEFAULT, + "Should be the right profile." + ); + Assert.ok( + profileData.profiles[0].default, + "Should be the old default profile." + ); + Assert.equal( + profileData.profiles[0].path, + root.leafName, + "Should be the correct path." + ); + Assert.equal( + profileData.profiles[1].name, + expectedName, + "Should be the right profile." + ); + Assert.ok( + !profileData.profiles[1].default, + "Should not be the old default profile." + ); + + let hash = xreDirProvider.getInstallHash(); + Assert.equal( + Object.keys(profileData.installs).length, + 2, + "Should be one known install." + ); + Assert.notEqual( + profileData.installs[hash].default, + root.leafName, + "Should have marked the original default profile as the default for this install." + ); + Assert.ok( + profileData.installs[hash].locked, + "Should have locked as we created the profile for this install." + ); + Assert.equal( + profileData.installs.otherinstall.default, + root.leafName, + "Should have left the other profile as the default for the other install." + ); + Assert.ok( + profileData.installs[hash].locked, + "Should still be locked to the other install." + ); +}); diff --git a/toolkit/profile/xpcshell/test_snap.js b/toolkit/profile/xpcshell/test_snap.js new file mode 100644 index 0000000000..c29c85089e --- /dev/null +++ b/toolkit/profile/xpcshell/test_snap.js @@ -0,0 +1,65 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that an old-style default profile not previously used by this build gets + * used in a snap environment. + */ + +add_task(async () => { + let defaultProfile = makeRandomProfileDir("default"); + + // Just pretend this profile was last used by something in the profile dir. + let greDir = gProfD.clone(); + greDir.append("app"); + writeCompatibilityIni(defaultProfile, greDir, greDir); + + writeProfilesIni({ + profiles: [ + { + name: PROFILE_DEFAULT, + path: defaultProfile.leafName, + default: true, + }, + ], + }); + + let { profile: selectedProfile, didCreate } = selectStartupProfile(); + checkStartupReason("default"); + + let profileData = readProfilesIni(); + let installsINI = gDataHome.clone(); + installsINI.append("installs.ini"); + Assert.ok( + !installsINI.exists(), + "Installs database should not have been created." + ); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 1, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, PROFILE_DEFAULT, "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + checkProfileService(profileData); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok( + selectedProfile.rootDir.equals(defaultProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, PROFILE_DEFAULT); +}); diff --git a/toolkit/profile/xpcshell/test_snap_empty.js b/toolkit/profile/xpcshell/test_snap_empty.js new file mode 100644 index 0000000000..80b1de9569 --- /dev/null +++ b/toolkit/profile/xpcshell/test_snap_empty.js @@ -0,0 +1,26 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that from a clean slate snap builds create an appropriate profile. + */ + +add_task(async () => { + let service = getProfileService(); + let { profile, didCreate } = selectStartupProfile(); + checkStartupReason("firstrun-created-default"); + + Assert.ok(didCreate, "Should have created a new profile."); + Assert.equal( + profile.name, + PROFILE_DEFAULT, + "Should have used the normal name." + ); + if (AppConstants.MOZ_DEV_EDITION) { + Assert.equal(service.profileCount, 2, "Should be two profiles."); + } else { + Assert.equal(service.profileCount, 1, "Should be only one profile."); + } + + checkProfileService(); +}); diff --git a/toolkit/profile/xpcshell/test_snatch_environment.js b/toolkit/profile/xpcshell/test_snatch_environment.js new file mode 100644 index 0000000000..2241fb691c --- /dev/null +++ b/toolkit/profile/xpcshell/test_snatch_environment.js @@ -0,0 +1,101 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that the environment variables are used to select a profile and that + * on the first run of a dedicated profile build we snatch it if it was the + * default profile. + */ + +add_task(async () => { + let root = makeRandomProfileDir("foo"); + let local = gDataHomeLocal.clone(); + local.append("foo"); + + writeCompatibilityIni(root); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: PROFILE_DEFAULT, + path: root.leafName, + default: true, + }, + { + name: "Profile2", + path: "Path2", + }, + { + name: "Profile3", + path: "Path3", + }, + ], + // Another install is using the profile but it isn't locked. + installs: { + otherinstall: { + default: root.leafName, + }, + }, + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + Services.env.set("XRE_PROFILE_PATH", root.path); + Services.env.set("XRE_PROFILE_LOCAL_PATH", local.path); + + let { rootDir, localDir, profile, didCreate } = selectStartupProfile(); + checkStartupReason("restart-claimed-default"); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok(rootDir.equals(root), "Should have selected the right root dir."); + Assert.ok( + localDir.equals(local), + "Should have selected the right local dir." + ); + Assert.ok(profile, "A named profile matches this."); + Assert.equal(profile.name, PROFILE_DEFAULT, "The right profile was matched."); + + let service = getProfileService(); + Assert.equal( + service.defaultProfile, + profile, + "Should be the default profile." + ); + Assert.equal( + service.currentProfile, + profile, + "Should be the current profile." + ); + + profileData = readProfilesIni(); + Assert.equal( + profileData.profiles[0].name, + PROFILE_DEFAULT, + "Should be the right profile." + ); + Assert.ok( + profileData.profiles[0].default, + "Should still be the old default profile." + ); + + let hash = xreDirProvider.getInstallHash(); + // The info about the other install will have been removed so it goes through first run on next startup. + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should be one known install." + ); + Assert.equal( + profileData.installs[hash].default, + root.leafName, + "Should have marked the original default profile as the default for this install." + ); + Assert.ok( + !profileData.installs[hash].locked, + "Should not have locked as we're not the default app." + ); +}); diff --git a/toolkit/profile/xpcshell/test_snatch_environment_default.js b/toolkit/profile/xpcshell/test_snatch_environment_default.js new file mode 100644 index 0000000000..d93a0cfc12 --- /dev/null +++ b/toolkit/profile/xpcshell/test_snatch_environment_default.js @@ -0,0 +1,101 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that the environment variables are used to select a profile and that + * on the first run of a dedicated profile build we snatch it if it was the + * default profile and lock it when we're the default app. + */ + +add_task(async () => { + gIsDefaultApp = true; + + let root = makeRandomProfileDir("foo"); + let local = gDataHomeLocal.clone(); + local.append("foo"); + + writeCompatibilityIni(root); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [ + { + name: PROFILE_DEFAULT, + path: root.leafName, + default: true, + }, + { + name: "Profile2", + path: "Path2", + }, + { + name: "Profile3", + path: "Path3", + }, + ], + // Another install is using the profile but it isn't locked. + installs: { + otherinstall: { + default: root.leafName, + }, + }, + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + Services.env.set("XRE_PROFILE_PATH", root.path); + Services.env.set("XRE_PROFILE_LOCAL_PATH", local.path); + + let { rootDir, localDir, profile, didCreate } = selectStartupProfile(); + checkStartupReason("restart-claimed-default"); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok(rootDir.equals(root), "Should have selected the right root dir."); + Assert.ok( + localDir.equals(local), + "Should have selected the right local dir." + ); + Assert.ok(!!profile, "A named profile matches this."); + Assert.equal(profile.name, PROFILE_DEFAULT, "The right profile was matched."); + + let service = getProfileService(); + Assert.ok( + service.defaultProfile === profile, + "Should be the default profile." + ); + Assert.ok( + service.currentProfile === profile, + "Should be the current profile." + ); + + profileData = readProfilesIni(); + Assert.equal( + profileData.profiles[0].name, + PROFILE_DEFAULT, + "Should be the right profile." + ); + Assert.ok( + profileData.profiles[0].default, + "Should still be the old default profile." + ); + + let hash = xreDirProvider.getInstallHash(); + // The info about the other install will have been removed so it goes through first run on next startup. + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should be one known install." + ); + Assert.equal( + profileData.installs[hash].default, + root.leafName, + "Should have marked the original default profile as the default for this install." + ); + Assert.ok( + profileData.installs[hash].locked, + "Should have locked as we're the default app." + ); +}); diff --git a/toolkit/profile/xpcshell/test_startswithlast.js b/toolkit/profile/xpcshell/test_startswithlast.js new file mode 100644 index 0000000000..1b1fef4415 --- /dev/null +++ b/toolkit/profile/xpcshell/test_startswithlast.js @@ -0,0 +1,28 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that if profiles.ini is set to not start with the last profile then + * we show the profile manager in preference to assigning the old default. + */ + +add_task(async () => { + let defaultProfile = makeRandomProfileDir("default"); + + writeCompatibilityIni(defaultProfile); + + writeProfilesIni({ + options: { + startWithLastProfile: false, + }, + profiles: [ + { + name: PROFILE_DEFAULT, + path: defaultProfile.leafName, + default: true, + }, + ], + }); + + testStartsProfileManager(); +}); diff --git a/toolkit/profile/xpcshell/test_steal_inuse.js b/toolkit/profile/xpcshell/test_steal_inuse.js new file mode 100644 index 0000000000..68263ceae1 --- /dev/null +++ b/toolkit/profile/xpcshell/test_steal_inuse.js @@ -0,0 +1,77 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that an old-style default profile previously used by this build but + * that has already been claimed by a different build gets stolen by this build. + */ + +add_task(async () => { + let defaultProfile = makeRandomProfileDir("default"); + + writeCompatibilityIni(defaultProfile); + + writeProfilesIni({ + profiles: [ + { + name: PROFILE_DEFAULT, + path: defaultProfile.leafName, + default: true, + }, + ], + installs: { + otherhash: { + default: defaultProfile.leafName, + }, + }, + }); + + let { profile: selectedProfile, didCreate } = selectStartupProfile(); + checkStartupReason("firstrun-claimed-default"); + + let hash = xreDirProvider.getInstallHash(); + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 1, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, PROFILE_DEFAULT, "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should only be one known installs." + ); + Assert.equal( + profileData.installs[hash].default, + defaultProfile.leafName, + "Should have taken the original default profile as the default for the current install." + ); + Assert.ok( + !profileData.installs[hash].locked, + "Should not have locked as we're not the default app." + ); + + checkProfileService(profileData); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok( + selectedProfile.rootDir.equals(defaultProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, PROFILE_DEFAULT); +}); diff --git a/toolkit/profile/xpcshell/test_update_selected_dedicated.js b/toolkit/profile/xpcshell/test_update_selected_dedicated.js new file mode 100644 index 0000000000..5a7c734e90 --- /dev/null +++ b/toolkit/profile/xpcshell/test_update_selected_dedicated.js @@ -0,0 +1,80 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that an old-style default profile previously used by this build gets + * updated to a dedicated profile for this build. + */ + +add_task(async () => { + let defaultProfile = makeRandomProfileDir("default"); + + writeCompatibilityIni(defaultProfile); + + writeProfilesIni({ + profiles: [ + { + name: PROFILE_DEFAULT, + path: defaultProfile.leafName, + default: true, + }, + ], + }); + + let { profile: selectedProfile, didCreate } = selectStartupProfile(); + checkStartupReason("firstrun-claimed-default"); + + let { databaseVersion, profileCount } = getTelemetryScalars(); + Assert.equal( + databaseVersion, + "1", + "Old database file was present at startup." + ); + Assert.equal(profileCount, 1, "Should be one profile."); + + let hash = xreDirProvider.getInstallHash(); + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 1, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, PROFILE_DEFAULT, "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should be only one known install." + ); + Assert.equal( + profileData.installs[hash].default, + defaultProfile.leafName, + "Should have marked the original default profile as the default for this install." + ); + Assert.ok( + !profileData.installs[hash].locked, + "Should not have locked as we're not the default app." + ); + + checkProfileService(profileData); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok( + selectedProfile.rootDir.equals(defaultProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, PROFILE_DEFAULT); +}); diff --git a/toolkit/profile/xpcshell/test_update_unknown_dedicated.js b/toolkit/profile/xpcshell/test_update_unknown_dedicated.js new file mode 100644 index 0000000000..1fd8c30fbf --- /dev/null +++ b/toolkit/profile/xpcshell/test_update_unknown_dedicated.js @@ -0,0 +1,85 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that an old-style default profile not previously used by any build + * doesn't get updated to a dedicated profile for this build and we don't set + * the flag to show the user info about dedicated profiles. + */ + +add_task(async () => { + let hash = xreDirProvider.getInstallHash(); + let defaultProfile = makeRandomProfileDir("default"); + + writeProfilesIni({ + profiles: [ + { + name: PROFILE_DEFAULT, + path: defaultProfile.leafName, + default: true, + }, + ], + }); + + let { profile: selectedProfile, didCreate } = selectStartupProfile(); + checkStartupReason("firstrun-created-default"); + + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 2, + "Should have the right number of profiles." + ); + + // Since there is already a profile with the desired name on dev-edition, a + // unique version will be used. + let expectedName = AppConstants.MOZ_DEV_EDITION + ? `${DEDICATED_NAME}-1` + : DEDICATED_NAME; + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, PROFILE_DEFAULT, "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + profile = profileData.profiles[1]; + Assert.equal(profile.name, expectedName, "Should have the right name."); + Assert.notEqual( + profile.path, + defaultProfile.leafName, + "Should not be the original default profile." + ); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should be a default for installs." + ); + Assert.equal( + profileData.installs[hash].default, + profile.path, + "Should have the right default profile." + ); + Assert.ok( + profileData.installs[hash].locked, + "Should have locked as we created this profile for this install." + ); + + checkProfileService(profileData); + + Assert.ok(didCreate, "Should have created a new profile."); + Assert.ok( + !selectedProfile.rootDir.equals(defaultProfile), + "Should not be using the old directory." + ); + Assert.equal(selectedProfile.name, expectedName); +}); diff --git a/toolkit/profile/xpcshell/test_update_unselected_dedicated.js b/toolkit/profile/xpcshell/test_update_unselected_dedicated.js new file mode 100644 index 0000000000..4aa56eaaac --- /dev/null +++ b/toolkit/profile/xpcshell/test_update_unselected_dedicated.js @@ -0,0 +1,89 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests that an old-style default profile not previously used by this build gets + * ignored. + */ + +add_task(async () => { + let hash = xreDirProvider.getInstallHash(); + let defaultProfile = makeRandomProfileDir("default"); + + // Just pretend this profile was last used by something in the profile dir. + let greDir = gProfD.clone(); + greDir.append("app"); + writeCompatibilityIni(defaultProfile, greDir, greDir); + + writeProfilesIni({ + profiles: [ + { + name: PROFILE_DEFAULT, + path: defaultProfile.leafName, + default: true, + }, + ], + }); + + let { profile: selectedProfile, didCreate } = selectStartupProfile(); + checkStartupReason("firstrun-skipped-default"); + + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 2, + "Should have the right number of profiles." + ); + + // Since there is already a profile with the desired name on dev-edition, a + // unique version will be used. + let expectedName = AppConstants.MOZ_DEV_EDITION + ? `${DEDICATED_NAME}-1` + : DEDICATED_NAME; + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, PROFILE_DEFAULT, "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + profile = profileData.profiles[1]; + Assert.equal(profile.name, expectedName, "Should have the right name."); + Assert.notEqual( + profile.path, + defaultProfile.leafName, + "Should not be the original default profile." + ); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 1, + "Should be a default for this install." + ); + Assert.equal( + profileData.installs[hash].default, + profile.path, + "Should have marked the new profile as the default for this install." + ); + Assert.ok( + profileData.installs[hash].locked, + "Should have locked as we created this profile for this install." + ); + + checkProfileService(profileData); + + Assert.ok(didCreate, "Should have created a new profile."); + Assert.ok( + !selectedProfile.rootDir.equals(defaultProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, expectedName); +}); diff --git a/toolkit/profile/xpcshell/test_use_dedicated.js b/toolkit/profile/xpcshell/test_use_dedicated.js new file mode 100644 index 0000000000..d83b6fc3a7 --- /dev/null +++ b/toolkit/profile/xpcshell/test_use_dedicated.js @@ -0,0 +1,107 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that if installs.ini lists a profile we use it as the default. + */ + +add_task(async () => { + let hash = xreDirProvider.getInstallHash(); + let defaultProfile = makeRandomProfileDir("default"); + let dedicatedProfile = makeRandomProfileDir("dedicated"); + let devProfile = makeRandomProfileDir("devedition"); + + // Make sure we don't steal the old-style default. + writeCompatibilityIni(defaultProfile); + + writeProfilesIni({ + profiles: [ + { + name: "default", + path: defaultProfile.leafName, + default: true, + }, + { + name: "dedicated", + path: dedicatedProfile.leafName, + }, + { + name: "dev-edition-default", + path: devProfile.leafName, + }, + ], + installs: { + [hash]: { + default: dedicatedProfile.leafName, + }, + otherhash: { + default: "foobar", + }, + }, + }); + + let { profile: selectedProfile, didCreate } = selectStartupProfile(); + checkStartupReason("default"); + let { databaseVersion, profileCount } = getTelemetryScalars(); + Assert.equal( + databaseVersion, + "2", + "New database file was present at startup." + ); + Assert.equal(profileCount, 3, "Should be three profiles present."); + + let profileData = readProfilesIni(); + + Assert.ok( + profileData.options.startWithLastProfile, + "Should be set to start with the last profile." + ); + Assert.equal( + profileData.profiles.length, + 3, + "Should have the right number of profiles." + ); + + let profile = profileData.profiles[0]; + Assert.equal(profile.name, `dedicated`, "Should have the right name."); + Assert.equal( + profile.path, + dedicatedProfile.leafName, + "Should be the expected dedicated profile." + ); + Assert.ok(!profile.default, "Should not be marked as the old-style default."); + + profile = profileData.profiles[1]; + Assert.equal(profile.name, "default", "Should have the right name."); + Assert.equal( + profile.path, + defaultProfile.leafName, + "Should be the original default profile." + ); + Assert.ok(profile.default, "Should be marked as the old-style default."); + + Assert.equal( + Object.keys(profileData.installs).length, + 2, + "Should be two known installs." + ); + Assert.equal( + profileData.installs[hash].default, + dedicatedProfile.leafName, + "Should have kept the default for this install." + ); + Assert.equal( + profileData.installs.otherhash.default, + "foobar", + "Should have kept the default for the other install." + ); + + checkProfileService(profileData); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok( + selectedProfile.rootDir.equals(dedicatedProfile), + "Should be using the right directory." + ); + Assert.equal(selectedProfile.name, "dedicated"); +}); diff --git a/toolkit/profile/xpcshell/xpcshell.toml b/toolkit/profile/xpcshell/xpcshell.toml new file mode 100644 index 0000000000..00c00cc6c4 --- /dev/null +++ b/toolkit/profile/xpcshell/xpcshell.toml @@ -0,0 +1,97 @@ +[DEFAULT] +head = "head.js" +skip-if = ["os == 'android'"] + +["test_check_backup.js"] + +["test_claim_locked.js"] + +["test_clean.js"] + +["test_conflict_installs.js"] + +["test_conflict_profiles.js"] + +["test_create_default.js"] + +["test_fix_directory_case.js"] + +["test_ignore_legacy_directory.js"] + +["test_invalid_descriptor.js"] + +["test_legacy_empty.js"] + +["test_legacy_select.js"] + +["test_lock.js"] + +["test_missing_profilesini.js"] + +["test_new_default.js"] + +["test_previous_dedicated.js"] + +["test_profile_reset.js"] + +["test_remove.js"] + +["test_remove_default.js"] + +["test_select_backgroundtasks_ephemeral.js"] + +["test_select_backgroundtasks_not_ephemeral_create.js"] + +["test_select_backgroundtasks_not_ephemeral_exists.js"] + +["test_select_default.js"] + +["test_select_environment.js"] + +["test_select_environment_named.js"] + +["test_select_missing.js"] + +["test_select_named.js"] + +["test_select_noname.js"] + +["test_select_profile_argument.js"] + +["test_select_profile_argument_new.js"] + +["test_select_profilemanager.js"] + +["test_single_profile_selected.js"] +skip-if = ["devedition"] + +["test_single_profile_unselected.js"] +skip-if = ["devedition"] + +["test_skip_locked_environment.js"] + +["test_snap.js"] +snap = true +run-if = ["os == 'linux'"] +skip-if = ["appname == 'thunderbird'"] + +["test_snap_empty.js"] +snap = true +run-if = ["os == 'linux'"] +skip-if = ["appname == 'thunderbird'"] + +["test_snatch_environment.js"] + +["test_snatch_environment_default.js"] + +["test_startswithlast.js"] + +["test_steal_inuse.js"] + +["test_update_selected_dedicated.js"] + +["test_update_unknown_dedicated.js"] + +["test_update_unselected_dedicated.js"] + +["test_use_dedicated.js"] -- cgit v1.2.3