Test suite: add --config-dir isolation, --smoke-test alias, and a cold-start/close gate step

Adopted from Andre's RemSound-smoke-test-agent-brief.md - the gaps our pack didn't
already cover:

- --config-dir <folder>: redirect ALL user state (config, profiles, logs, cue
  sounds) to an explicit folder for this process only, applied at the very start of
  Program.Main before the layout migration runs. Lets a test exercise a real build
  without touching the user's live settings (the brief's safety rule 1). Works with
  every command. AppConfig gains SetUserDataDirectoryOverride / an override on
  UserDataDirectory; CommandLine.TryGetConfigDir parses it early.
- --smoke-test / --smoketest: alias for --selftest, matching the brief's vocabulary.
- run-tests.ps1: a cold-start + clean-close smoke (brief baseline steps 3-4) -
  launches the GUI minimized against an isolated --config-dir, confirms it stays up,
  that it used the isolated folder (real settings untouched), and that --close shuts
  it down with no orphan process. SKIPs cleanly if a RemSound instance is already
  running (machine-wide single-instance lock).

Manual + --help updated for both switches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-06-12 23:12:22 +01:00
co-authored by Claude Opus 4.8
parent 141c5e8ce1
commit 832ed40bf7
6 changed files with 93 additions and 5 deletions
+25 -2
View File
@@ -40,6 +40,25 @@ internal static class CommandLine
[DllImport("kernel32.dll")] private static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
/// <summary>The folder given after <c>--config-dir</c>, or null. Read at the very start of
/// <see cref="Program"/> - before the layout migration and any config/profile/log/sound access -
/// so it can redirect ALL user state via <see cref="AppConfig.SetUserDataDirectoryOverride"/>.
/// Applies to every command (e.g. <c>--selftest --config-dir</c>, <c>--diagnostics --config-dir</c>)
/// and to a normal GUI launch, so a test can exercise a real build without touching live settings.</summary>
public static bool TryGetConfigDir(string[] args, out string dir)
{
dir = "";
for (var i = 0; i < args.Length - 1; i++)
{
if (args[i].Equals("--config-dir", StringComparison.OrdinalIgnoreCase) && !args[i + 1].StartsWith('-'))
{
dir = args[i + 1];
return !string.IsNullOrWhiteSpace(dir);
}
}
return false;
}
/// <summary>
/// Process the command line. Returns a non-null exit code when a do-and-exit command ran (the
/// caller should <c>Environment.Exit</c> it); returns null to continue into the GUI launch with
@@ -61,7 +80,7 @@ internal static class CommandLine
return WithConsole(PrintVersion);
case "--devices": case "--list-devices":
return WithConsole(() => { WriteDevices(Console.Out); return 0; });
case "--selftest": case "--self-test":
case "--selftest": case "--self-test": case "--smoke-test": case "--smoketest":
return WithConsole(() => SelfTest.Run(args));
case "--diagnostics": case "--diag":
return WithConsole(() => RunDiagnostics(ValueAfter(args, raw)));
@@ -130,7 +149,7 @@ internal static class CommandLine
Console.WriteLine(" --devices List all microphones, outputs and ASIO drivers,");
Console.WriteLine(" with their formats and device ids.");
Console.WriteLine(" --selftest [--seconds N] Run the built-in self-test - a localhost audio");
Console.WriteLine(" round-trip plus checks of encryption, the wire format,");
Console.WriteLine(" (or --smoke-test) round-trip plus checks of encryption, the wire format,");
Console.WriteLine(" settings, profiles and bundled files - and report PASS/FAIL.");
Console.WriteLine(" --diagnostics [path] Write a diagnostics report (version, config, profiles,");
Console.WriteLine(" devices, mic-privacy check, recent log) and exit. With");
@@ -145,6 +164,10 @@ internal static class CommandLine
Console.WriteLine(" --connect <ip[:port]> Start and connect to a peer at this address. With no");
Console.WriteLine(" --profile, starts on a fresh profile connected to it.");
Console.WriteLine(" --minimized, --tray Start minimized to the notification area.");
Console.WriteLine(" --config-dir <folder> Use an explicit folder for this run's settings, profiles,");
Console.WriteLine(" logs and sounds, instead of the usual location. Lets a test");
Console.WriteLine(" exercise RemSound without touching your real settings.");
Console.WriteLine(" Works with any command (e.g. --selftest --config-dir ...).");
Console.WriteLine();
Console.WriteLine("Examples:");
Console.WriteLine(" RemSound.exe --devices");
+10
View File
@@ -25,6 +25,16 @@ internal static class Program
return;
}
// --config-dir <folder> (test / portable isolation): redirect ALL user state - config,
// profiles, logs, cue sounds - to an explicit folder for THIS process only. Applied first,
// before the layout migration and sound consolidation below read or write the default
// location, so a smoke test can run a real build without touching the user's settings
// (smoke-test brief, safety rule 1).
if (CommandLine.TryGetConfigDir(args, out var configDir))
{
AppConfig.SetUserDataDirectoryOverride(configDir);
}
// SustainedLowLatency tells the GC to avoid full (gen 2) collections while audio is streaming.
// Gen 0/1 collections still happen but are sub-millisecond; the long pauses that were causing
// the receiver to fall behind in clusters of 4-5 underruns at a time were almost certainly