From a52f08c1886ec2b5f2e17944bf79a679f387f22b Mon Sep 17 00:00:00 2001 From: Ednunp <29843396+Ednunp@users.noreply.github.com> Date: Sun, 12 Jul 2026 19:16:08 +0100 Subject: [PATCH] Tests: functional profile round-trip through the real main-window controls Local checkpoint - NOT for public release. Toward Ed's "every control and every function tested" goal: a new self-test applies a profile to the ACTUAL main-window controls (via an internal ApplyThenCaptureForTest seam on the headless form) and reads it straight back, asserting every persisted value survived - volume, mute, send/receive toggles, peer-shaping master, send mode, send-all- applications, and the selected applications. This exercises each control's load AND save logic, not just that it exists (which the accessibility audit already covers). The apply path pops the "set a password to stream" dialog when enabling send with no password (would hang a headless test); the seam sets the existing suppressStreamingPasswordGate around the apply, same gate the app uses internally. Gate 25/25. Co-Authored-By: Claude Opus 4.8 --- src/RemSound.App/MainForm.cs | 15 +++++++++++ src/RemSound.App/SelfTest.cs | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/RemSound.App/MainForm.cs b/src/RemSound.App/MainForm.cs index 0b2d995..dd02fda 100644 --- a/src/RemSound.App/MainForm.cs +++ b/src/RemSound.App/MainForm.cs @@ -8172,6 +8172,21 @@ public sealed class MainForm : Form return profile; } + /// Test-only: push a profile INTO the real controls and read it straight back OUT, so a + /// self-test can prove every persisted control both loads and saves correctly. Headless forms only + /// (a real form would try to reconnect peers etc.); pass a profile with no peers. + internal Profile ApplyThenCaptureForTest(Profile input) + { + pendingProfile = input; + settings.ApplyProfile(input); + // Suppress the interactive "set a password to stream" gate that a real user apply would show — + // it would pop a modal dialog and hang the headless test. We only care about control round-trip. + suppressStreamingPasswordGate = true; + try { ApplyPendingProfileToControls(); } + finally { suppressStreamingPasswordGate = false; } + return BuildCurrentProfile(input.Title); + } + /// Common save body — gathers all current state into a Profile and writes it. /// On success, becomes the active profile (sets currentProfileTitle, updates window /// title, refreshes button visibility, and shows a confirmation popup). diff --git a/src/RemSound.App/SelfTest.cs b/src/RemSound.App/SelfTest.cs index 5bb63ba..40d0304 100644 --- a/src/RemSound.App/SelfTest.cs +++ b/src/RemSound.App/SelfTest.cs @@ -75,6 +75,7 @@ internal static class SelfTest RunStep(results, "Bundled resources present", ResourcesPresent); RunStep(results, "Dialog accessibility (names + mnemonics)", AccessibilityAudit); RunStep(results, "Main window coverage (all tabs + controls)", MainWindowCoverage); + RunStep(results, "Main window profile round-trip (controls load + save)", MainWindowProfileRoundTrip); var failed = results.Count(r => r.Status == "FAIL"); var skipped = results.Count(r => r.Status == "SKIP"); @@ -1050,6 +1051,53 @@ internal static class SelfTest finally { try { form.Dispose(); } catch { /* ignore */ } } } + /// Functional round-trip through the REAL main-window controls: apply a profile to the + /// controls, read it back, and assert every persisted value survived — proving each control's load + /// AND save logic, not just that it exists. Uses the headless form with no peers (so nothing tries + /// to connect). Device ticks need real hardware ids so they're covered by the profile-store + /// round-trip test instead; this covers the hardware-independent controls. + private static string? MainWindowProfileRoundTrip() + { + MainForm mf; + try { mf = new MainForm(null, RemSound.Core.Profile.NewBlank(), null, null, headless: true); } + catch (Exception ex) { return Skip($"headless MainForm could not be constructed: {ex.GetType().Name}: {ex.Message}"); } + + using (mf) + { + var input = new Profile + { + Title = "roundtrip", + Volume = 42, + Muted = true, + ReceiveAudioOn = true, + SendAudioOn = true, + EnableAllPeerShaping = true, + WasapiSendMode = "applications", + SendAllApplications = false, + }; + input.SelectedSendApplications.Add("vlc"); + input.SelectedSendApplications.Add("firefox"); + + var back = mf.ApplyThenCaptureForTest(input); + + Check(back.Volume == 42, $"volume must round-trip through the controls (got {back.Volume})"); + Check(back.Muted, "mute must round-trip through the controls"); + Check(back.ReceiveAudioOn && back.SendAudioOn, "send/receive toggles must round-trip"); + Check(back.EnableAllPeerShaping, "the peer-shaping master switch must round-trip"); + + var covered = "volume, mute, send/receive, shaping"; + if (RemSound.Sender.ProcessLoopbackCapture.IsSupported) + { + Check(back.WasapiSendMode == "applications", $"send mode must round-trip (got {back.WasapiSendMode})"); + Check(!back.SendAllApplications, "the send-all-applications toggle must round-trip"); + Check(back.SelectedSendApplications.Contains("vlc") && back.SelectedSendApplications.Contains("firefox"), + "the selected applications must round-trip through the app list"); + covered += ", send-mode, apps"; + } + return $"round-tripped through the real controls: {covered}"; + } + } + private static int CountControls(Control root, Func predicate) { var n = 0;