using RioJoy.Core.Profiles; using Xunit; namespace RioJoy.Core.Tests.Profiles; public class AutoSwitchTests { private static AppConfig Config() => new() { NativeGameExecutables = { "firestorm.exe", "RedPlanet" }, Profiles = { new RioProfile { Name = "Doom", MatchExecutables = { "doom.exe", "doom2" } }, new RioProfile { Name = "Quake", MatchExecutables = { "quake.exe" } }, }, }; [Theory] [InlineData("firestorm.exe")] [InlineData("FIRESTORM.EXE")] [InlineData(@"C:\games\Firestorm\firestorm.exe")] [InlineData("redplanet.exe")] // configured without extension, matched with one public void NativeGame_Yields(string exe) { Assert.Equal(SwitchMode.Yield, AutoSwitchResolver.Resolve(Config(), exe).Mode); } [Fact] public void SupportedGame_Activates_ItsProfile() { SwitchDecision d = AutoSwitchResolver.Resolve(Config(), @"D:\Games\doom.exe"); Assert.Equal(SwitchMode.Activate, d.Mode); Assert.Equal("Doom", d.Profile!.Name); } [Fact] public void Unknown_IsIdle_WhenNoNeutral() { Assert.Equal(SwitchMode.Idle, AutoSwitchResolver.Resolve(Config(), "explorer.exe").Mode); Assert.Equal(SwitchMode.Idle, AutoSwitchResolver.Resolve(Config(), null).Mode); } [Fact] public void Unknown_ActivatesNeutral_WhenConfigured() { AppConfig config = Config(); config.NeutralProfileName = "Doom"; SwitchDecision d = AutoSwitchResolver.Resolve(config, "explorer.exe"); Assert.Equal(SwitchMode.Activate, d.Mode); Assert.Equal("Doom", d.Profile!.Name); } [Fact] public void Native_Wins_OverProfileMatch() { AppConfig config = Config(); // A profile also claims the native exe; native yield must still win. config.Profiles[0].MatchExecutables.Add("firestorm.exe"); Assert.Equal(SwitchMode.Yield, AutoSwitchResolver.Resolve(config, "firestorm.exe").Mode); } [Fact] public void Watcher_RaisesOnlyOnChange() { var config = Config(); string? exe = "doom.exe"; var provider = new FakeForeground(() => exe); var watcher = new AutoSwitchWatcher(provider, () => config); var changes = new List(); watcher.DecisionChanged += changes.Add; watcher.Poll(); // doom → Activate watcher.Poll(); // doom again → no change exe = "firestorm.exe"; watcher.Poll(); // native → Yield (change) exe = "firestorm.exe"; watcher.Poll(); // same → no change Assert.Equal(2, changes.Count); Assert.Equal(SwitchMode.Activate, changes[0].Mode); Assert.Equal(SwitchMode.Yield, changes[1].Mode); } [Fact] public void Reset_ReRaises_SameDecision_OnNextPoll() { var config = Config(); var provider = new FakeForeground(() => "doom.exe"); var watcher = new AutoSwitchWatcher(provider, () => config); var changes = new List(); watcher.DecisionChanged += changes.Add; watcher.Poll(); // doom → Activate (raise) watcher.Poll(); // same → no raise watcher.Reset(); // forget last decision (e.g. after an editor session) watcher.Poll(); // same decision, but re-raised because of Reset Assert.Equal(2, changes.Count); Assert.All(changes, c => Assert.Equal(SwitchMode.Activate, c.Mode)); } private sealed class FakeForeground(Func get) : IForegroundProcessProvider { public string? GetForegroundExecutable() => get(); } }