diff --git a/Keybinding.Test/SaveWithoutInitializeTests.cs b/Keybinding.Test/SaveWithoutInitializeTests.cs index ff09e5d..1359249 100644 --- a/Keybinding.Test/SaveWithoutInitializeTests.cs +++ b/Keybinding.Test/SaveWithoutInitializeTests.cs @@ -113,6 +113,75 @@ public async Task SaveAsync_WithoutInitialize_KeepsStoredCommandsAndActiveProfil Assert.AreEqual("file.save", reloaded.Keybindings.ExecuteChord("default", Chord.Parse("Ctrl+S"))); } + [TestMethod] + public async Task SaveAsync_TwiceWithoutInitialize_KeepsStoredCommands() + { + { + using KeybindingManager manager = new(_testDataDirectory); + await manager.InitializeAsync().ConfigureAwait(false); + manager.Commands.RegisterCommand(new Command("file.save", "Save")); + manager.CreateDefaultProfile(); + manager.Keybindings.BindChord("file.save", Chord.Parse("Ctrl+S")); + await manager.SaveAsync().ConfigureAwait(false); + } + + { + using KeybindingManager manager = new(_testDataDirectory); + manager.Profiles.CreateProfile("other", "Other"); + await manager.SaveAsync().ConfigureAwait(false); + + // The first save carried file.save through; the second must not read it as unregistered here + await manager.SaveAsync().ConfigureAwait(false); + } + + using KeybindingManager reloaded = new(_testDataDirectory); + await reloaded.InitializeAsync().ConfigureAwait(false); + Assert.IsTrue(reloaded.Commands.IsCommandRegistered("file.save")); + Assert.AreEqual("file.save", reloaded.Keybindings.ExecuteChord("default", Chord.Parse("Ctrl+S"))); + } + + [TestMethod] + public async Task SaveAsync_TwiceAfterAnotherManagerSaved_KeepsItsCommand() + { + using KeybindingManager first = new(_testDataDirectory); + using KeybindingManager second = new(_testDataDirectory); + await first.InitializeAsync().ConfigureAwait(false); + await second.InitializeAsync().ConfigureAwait(false); + + first.Commands.RegisterCommand(new Command("a.cmd", "A")); + second.Commands.RegisterCommand(new Command("b.cmd", "B")); + second.Profiles.CreateProfile("bprof", "B profile"); + await second.SaveAsync().ConfigureAwait(false); + + await first.SaveAsync().ConfigureAwait(false); + await first.SaveAsync().ConfigureAwait(false); + + using KeybindingManager reloaded = new(_testDataDirectory); + await reloaded.InitializeAsync().ConfigureAwait(false); + Assert.IsTrue(reloaded.Commands.IsCommandRegistered("a.cmd")); + Assert.IsTrue(reloaded.Commands.IsCommandRegistered("b.cmd")); + Assert.IsTrue(reloaded.Profiles.ProfileExists("bprof")); + } + + [TestMethod] + public async Task SaveAsync_CommandUnregisteredAfterItsFirstSave_IsRemovedOnTheNext() + { + { + using KeybindingManager manager = new(_testDataDirectory); + manager.Commands.RegisterCommand(new Command("file.save", "Save")); + manager.Commands.RegisterCommand(new Command("tools.legacy", "Legacy")); + await manager.SaveAsync().ConfigureAwait(false); + + Assert.IsTrue(manager.Commands.UnregisterCommand("tools.legacy")); + await manager.SaveAsync().ConfigureAwait(false); + } + + using KeybindingManager reloaded = new(_testDataDirectory); + await reloaded.InitializeAsync().ConfigureAwait(false); + Assert.IsTrue(reloaded.Commands.IsCommandRegistered("file.save")); + Assert.IsFalse(reloaded.Commands.IsCommandRegistered("tools.legacy")); + } + [TestMethod] public async Task SaveAsync_CommandUnregisteredByManagerThatLoadedIt_IsRemoved() { diff --git a/Keybinding/KeybindingManager.cs b/Keybinding/KeybindingManager.cs index 268a3bf..ff77d97 100644 --- a/Keybinding/KeybindingManager.cs +++ b/Keybinding/KeybindingManager.cs @@ -152,7 +152,10 @@ .. storedCommands.Where(c => !commandIds.Contains(c.Id.ToString()) && !_persiste lock (_persistedStateLock) { _persistedCommandIds.Clear(); - _persistedCommandIds.UnionWith(commandsToSave.Select(c => c.Id.ToString())); + // Only the commands registered here count as persisted by this manager. The stored ones it carried through + // belong to someone else; marking them too would make the next save read them as unregistered here and + // drop them from the file. + _persistedCommandIds.UnionWith(commandIds); } // Remove stored profiles that were deleted in memory, so they do not come back on the next load. Only