From c8e2d5ce6984b87755d955bd1d7220c978abb691 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 17 Nov 2025 00:53:08 +0000 Subject: [PATCH] Fixes #4391. Weird situation where ForceDriver with args doesn't persists on open scenario --- Examples/UICatalog/UICatalog.cs | 83 ++++++++++++++++++- Terminal.Gui/App/Application.Driver.cs | 11 ++- Terminal.Gui/App/ApplicationImpl.Driver.cs | 2 +- Terminal.Gui/App/ApplicationImpl.Lifecycle.cs | 5 +- .../UICatalog/ScenarioTests.cs | 15 ++++ 5 files changed, 111 insertions(+), 5 deletions(-) diff --git a/Examples/UICatalog/UICatalog.cs b/Examples/UICatalog/UICatalog.cs index 6f7a37139..f86dc3489 100644 --- a/Examples/UICatalog/UICatalog.cs +++ b/Examples/UICatalog/UICatalog.cs @@ -55,13 +55,31 @@ namespace UICatalog; /// public class UICatalog { - private static string? _forceDriver = null; + private static string? _forceDriver; + private static bool _forTesting; + private static bool _iterationHandlerRemoved; + private static string? _uiCatalogDriver; + private static string? _scenarioDriver; public static string LogFilePath { get; set; } = string.Empty; public static LoggingLevelSwitch LogLevelSwitch { get; } = new (); public const string LOGFILE_LOCATION = "logs"; public static UICatalogCommandLineOptions Options { get; set; } + public static (string? UiCatalogDriver, string? ScenarioDriver) Run (string [] args) + { + // Flag for testing + _forTesting = true; + + // Start app + Main (args); + + // Unset testing flag + _forTesting = false; + + return new (_uiCatalogDriver, _scenarioDriver); + } + private static int Main (string [] args) { Console.OutputEncoding = Encoding.Default; @@ -194,6 +212,8 @@ public class UICatalog UICatalogMain (Options); + Debug.Assert (Application.ForceDriver == string.Empty); + return 0; } @@ -247,6 +267,11 @@ public class UICatalog /// private static Scenario RunUICatalogTopLevel () { + if (_iterationHandlerRemoved) + { + return null!; + } + // Run UI Catalog UI. When it exits, if _selectedScenario is != null then // a Scenario was selected. Otherwise, the user wants to quit UI Catalog. @@ -255,7 +280,22 @@ public class UICatalog Application.Init (driverName: _forceDriver); - var top = Application.Run (); + Toplevel top; + + if (_forTesting) + { + top = new UICatalogTop (); + SessionToken sessionToken = Application.Begin (top); + UICatalogTop.CachedSelectedScenario = Scenario.GetScenarios () [0]; + Application.End (sessionToken); + + _uiCatalogDriver = Application.Driver!.GetName (); + } + else + { + top = Application.Run (); + } + top.Dispose (); Application.Shutdown (); VerifyObjectsWereDisposed (); @@ -412,6 +452,8 @@ public class UICatalog Application.InitializedChanged += ApplicationOnInitializedChanged; #endif + Application.ForceDriver = _forceDriver; + scenario.Main (); scenario.Dispose (); @@ -430,6 +472,43 @@ public class UICatalog if (e.Value) { sw.Start (); + + if (_forTesting) + { + int iterationCount = 0; + Key quitKey; + + Application.Iteration += OnApplicationOnIteration; + + void OnApplicationOnIteration (object? s, IterationEventArgs a) + { + iterationCount++; + + if (Application.Initialized) + { + // Press QuitKey + quitKey = Application.QuitKey; + + Logging.Trace ( + $"Attempting to quit with {quitKey} after {iterationCount} iterations."); + + try + { + Application.RaiseKeyDownEvent (quitKey); + } + catch (Exception ex) + { + Logging.Trace ( + $"Exception raising quit key: {ex.Message}"); + } + + Application.Iteration -= OnApplicationOnIteration; + _iterationHandlerRemoved = true; + + _scenarioDriver = Application.Driver?.GetName (); + } + } + } } else { diff --git a/Terminal.Gui/App/Application.Driver.cs b/Terminal.Gui/App/Application.Driver.cs index c08fb879f..87e795746 100644 --- a/Terminal.Gui/App/Application.Driver.cs +++ b/Terminal.Gui/App/Application.Driver.cs @@ -26,7 +26,16 @@ public static partial class Application // Driver abstractions public static string ForceDriver { get => ApplicationImpl.Instance.ForceDriver; - set => ApplicationImpl.Instance.ForceDriver = value; + set + { + if (!string.IsNullOrEmpty (ApplicationImpl.Instance.ForceDriver)) + { + // ForceDriver cannot be changed if it has a valid value + return; + } + + ApplicationImpl.Instance.ForceDriver = value; + } } /// diff --git a/Terminal.Gui/App/ApplicationImpl.Driver.cs b/Terminal.Gui/App/ApplicationImpl.Driver.cs index 36679b2b0..88678b85e 100644 --- a/Terminal.Gui/App/ApplicationImpl.Driver.cs +++ b/Terminal.Gui/App/ApplicationImpl.Driver.cs @@ -35,7 +35,7 @@ public partial class ApplicationImpl bool factoryIsFake = _componentFactory is IComponentFactory; // Then check driverName - bool nameIsWindows = driverName?.Contains ("win", StringComparison.OrdinalIgnoreCase) ?? false; + bool nameIsWindows = driverName?.Contains ("windows", StringComparison.OrdinalIgnoreCase) ?? false; bool nameIsDotNet = driverName?.Contains ("dotnet", StringComparison.OrdinalIgnoreCase) ?? false; bool nameIsUnix = driverName?.Contains ("unix", StringComparison.OrdinalIgnoreCase) ?? false; bool nameIsFake = driverName?.Contains ("fake", StringComparison.OrdinalIgnoreCase) ?? false; diff --git a/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs b/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs index 0a7795833..2be859831 100644 --- a/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs +++ b/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs @@ -231,7 +231,10 @@ public partial class ApplicationImpl // === 9. Clear graphics === Sixel.Clear (); - // === 10. Reset synchronization context === + // === 10. Reset ForceDriver === + ForceDriver = string.Empty; + + // === 11. Reset synchronization context === // IMPORTANT: Always reset sync context, even if not initialized // This ensures cleanup works correctly even if Shutdown is called without Init // Reset synchronization context to allow the user to run async/await, diff --git a/Tests/IntegrationTests/UICatalog/ScenarioTests.cs b/Tests/IntegrationTests/UICatalog/ScenarioTests.cs index a7943d4e5..ba54736a6 100644 --- a/Tests/IntegrationTests/UICatalog/ScenarioTests.cs +++ b/Tests/IntegrationTests/UICatalog/ScenarioTests.cs @@ -665,4 +665,19 @@ public class ScenarioTests : TestsAllViews void LayoutCompleteHandler (object? sender, LayoutEventArgs args) { UpdateTitle (curView); } } + + [Fact] + public void ForceDriver_persists_On_Open_Scenario_With_Args () + { + string driverName = "fake"; + + string [] args = ["-d", driverName]; + + (string? UiCatalogDriver, string? ScenarioDriver) driverNames = global::UICatalog.UICatalog.Run (args); + + Assert.Equal (string.Empty, Application.ForceDriver); + Assert.Equal (driverName, driverNames.UiCatalogDriver); + Assert.Equal (driverName, driverNames.ScenarioDriver); + Assert.Equal (driverNames.UiCatalogDriver, driverNames.ScenarioDriver); + } }