diff --git a/Examples/Example/Example.cs b/Examples/Example/Example.cs index 15d4b5ce9..e85b80703 100644 --- a/Examples/Example/Example.cs +++ b/Examples/Example/Example.cs @@ -12,7 +12,7 @@ using Terminal.Gui.Views; [assembly: ExampleMetadata ("Simple Example", "A basic login form demonstrating Terminal.Gui fundamentals")] [assembly: ExampleCategory ("Getting Started")] -[assembly: ExampleDemoKeyStrokes (KeyStrokes = ["a", "d", "m", "i", "n", "Tab", "p", "a", "s", "s", "w", "o", "r", "d", "Enter"], Order = 1)] +[assembly: ExampleDemoKeyStrokes (KeyStrokes = ["a", "d", "m", "i", "n", "Tab", "p", "a", "s", "s", "w", "o", "r", "d", "Enter"], DelayMs = 500, Order = 1)] [assembly: ExampleDemoKeyStrokes (KeyStrokes = ["Enter"], DelayMs = 500, Order = 2)] [assembly: ExampleDemoKeyStrokes (KeyStrokes = ["Esc"], DelayMs = 100, Order = 3)] @@ -20,9 +20,6 @@ using Terminal.Gui.Views; ConfigurationManager.RuntimeConfig = """{ "Theme": "Light" }"""; ConfigurationManager.Enable (ConfigLocations.All); -// Setup automatic key injection for testing -ExampleContextInjector.SetupAutomaticInjection (); - // Check for test context to determine driver string? contextJson = Environment.GetEnvironmentVariable (ExampleContext.ENVIRONMENT_VARIABLE_NAME); string? driverName = null; @@ -33,8 +30,12 @@ if (!string.IsNullOrEmpty (contextJson)) driverName = context?.DriverName; } -IApplication app = Application.Create ().Init (driverName); +IApplication app = Application.Create (); +// Setup automatic key injection for testing +ExampleContextInjector.SetupAutomaticInjection (app); + +app.Init (driverName); app.Run (); // Dispose the app to clean up and enable Console.WriteLine below diff --git a/Examples/ExampleRunner/Program.cs b/Examples/ExampleRunner/Program.cs index 522e18867..53910d188 100644 --- a/Examples/ExampleRunner/Program.cs +++ b/Examples/ExampleRunner/Program.cs @@ -65,10 +65,11 @@ foreach (ExampleInfo example in examples) // Create context for running the example ExampleContext context = new () { - DriverName = "FakeDriver", - KeysToInject = ["Esc"], // Just press Esc to quit each example + KeysToInject = example.DemoKeyStrokes.OrderBy (ks => ks.Order) + .SelectMany (ks => ks.KeyStrokes) + .ToList (), TimeoutMs = 5000, - Mode = ExecutionMode.OutOfProcess + Mode = ExecutionMode.InProcess }; try diff --git a/Examples/FluentExample/Program.cs b/Examples/FluentExample/Program.cs index 4d92e6b53..478a3342e 100644 --- a/Examples/FluentExample/Program.cs +++ b/Examples/FluentExample/Program.cs @@ -13,8 +13,6 @@ using Terminal.Gui.Views; [assembly: ExampleDemoKeyStrokes (KeyStrokes = ["CursorDown", "CursorDown", "CursorRight", "Enter"], Order = 1)] [assembly: ExampleDemoKeyStrokes (KeyStrokes = ["Esc"], DelayMs = 100, Order = 2)] -// Setup automatic key injection for testing -ExampleContextInjector.SetupAutomaticInjection (); // Check for test context to determine driver string? contextJson = Environment.GetEnvironmentVariable (ExampleContext.ENVIRONMENT_VARIABLE_NAME); @@ -30,6 +28,9 @@ IApplication? app = Application.Create () .Init (driverName) .Run (); +// Setup automatic key injection for testing +ExampleContextInjector.SetupAutomaticInjection (app); + // Run the application with fluent API - automatically creates, runs, and disposes the runnable Color? result = app.GetResult () as Color?; diff --git a/Examples/RunnableWrapperExample/Program.cs b/Examples/RunnableWrapperExample/Program.cs index 7e575cf80..9f859ab5c 100644 --- a/Examples/RunnableWrapperExample/Program.cs +++ b/Examples/RunnableWrapperExample/Program.cs @@ -16,9 +16,6 @@ using Terminal.Gui.Views; [assembly: ExampleDemoKeyStrokes (KeyStrokes = ["Enter", "Esc"], DelayMs = 100, Order = 4)] [assembly: ExampleDemoKeyStrokes (KeyStrokes = ["Enter", "Esc"], DelayMs = 100, Order = 5)] -// Setup automatic key injection for testing -ExampleContextInjector.SetupAutomaticInjection (); - // Check for test context to determine driver string? contextJson = Environment.GetEnvironmentVariable (ExampleContext.ENVIRONMENT_VARIABLE_NAME); string? driverName = null; @@ -30,6 +27,10 @@ if (!string.IsNullOrEmpty (contextJson)) } IApplication app = Application.Create (); + +// Setup automatic key injection for testing +ExampleContextInjector.SetupAutomaticInjection (app); + app.Init (driverName); // Example 1: Use extension method with result extraction diff --git a/Terminal.Gui/Examples/ExampleContextInjector.cs b/Terminal.Gui/Examples/ExampleContextInjector.cs index a492b2017..1fab569f7 100644 --- a/Terminal.Gui/Examples/ExampleContextInjector.cs +++ b/Terminal.Gui/Examples/ExampleContextInjector.cs @@ -13,12 +13,13 @@ public static class ExampleContextInjector /// Sets up automatic key injection if a test context is present in the environment. /// Call this method before calling or . /// + /// /// /// This method is safe to call multiple times - it will only set up injection once. /// The actual key injection happens after the application is initialized, via the /// event. /// - public static void SetupAutomaticInjection () + public static void SetupAutomaticInjection (IApplication? app) { if (_initialized) { @@ -43,19 +44,15 @@ public static class ExampleContextInjector } // Subscribe to InitializedChanged to inject keys after initialization - Application.InitializedChanged += OnInitializedChanged; + app.SessionBegun += AppOnSessionBegun; return; - void OnInitializedChanged (object? sender, EventArgs e) + void AppOnSessionBegun (object? sender, SessionTokenEventArgs e) { - if (!e.Value) - { - return; - } // Application has been initialized, inject the keys - if (Application.Driver is null) + if (app.Driver is null) { return; } @@ -64,12 +61,12 @@ public static class ExampleContextInjector { if (Input.Key.TryParse (keyStr, out Input.Key? key) && key is { }) { - Application.Driver.EnqueueKeyEvent (key); + app.Keyboard.RaiseKeyDownEvent (key); } } // Unsubscribe after injecting keys once - Application.InitializedChanged -= OnInitializedChanged; + app.SessionBegun -= AppOnSessionBegun; } } } diff --git a/Terminal.Gui/Examples/ExampleRunner.cs b/Terminal.Gui/Examples/ExampleRunner.cs index f088ce053..aa96c3ac9 100644 --- a/Terminal.Gui/Examples/ExampleRunner.cs +++ b/Terminal.Gui/Examples/ExampleRunner.cs @@ -69,29 +69,47 @@ public static class ExampleRunner } ParameterInfo [] parameters = entryPoint.GetParameters (); - object? result = null; - if (parameters.Length == 0) + Task executionTask = Task.Run (() => { - result = entryPoint.Invoke (null, null); - } - else if (parameters.Length == 1 && parameters [0].ParameterType == typeof (string [])) - { - result = entryPoint.Invoke (null, [Array.Empty ()]); - } - else + object? result = null; + + if (parameters.Length == 0) + { + result = entryPoint.Invoke (null, null); + } + else if (parameters.Length == 1 && parameters [0].ParameterType == typeof (string [])) + { + result = entryPoint.Invoke (null, [Array.Empty ()]); + } + else + { + throw new InvalidOperationException ("Entry point has unsupported signature"); + } + + // If entry point returns Task, wait for it + if (result is Task task) + { + task.GetAwaiter ().GetResult (); + } + }); + + bool completed = executionTask.Wait (context.TimeoutMs); + + if (!completed) { + // reset terminal + Console.Clear (); return new () { Success = false, - ErrorMessage = "Entry point has unsupported signature" + TimedOut = true }; } - // If entry point returns Task, wait for it - if (result is Task task) + if (executionTask.Exception is { }) { - task.GetAwaiter ().GetResult (); + throw executionTask.Exception.GetBaseException (); } return new () diff --git a/Terminal.sln b/Terminal.sln index aacab4c01..99f06ecd2 100644 --- a/Terminal.sln +++ b/Terminal.sln @@ -127,6 +127,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentExample", "Examples\F EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RunnableWrapperExample", "Examples\RunnableWrapperExample\RunnableWrapperExample.csproj", "{26FDEE3C-9D1F-79A6-F48F-D0944C7F09F8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleRunner", "Examples\ExampleRunner\ExampleRunner.csproj", "{2CB35142-AAD4-D424-61D3-88F9C94AD62A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -209,6 +211,10 @@ Global {26FDEE3C-9D1F-79A6-F48F-D0944C7F09F8}.Debug|Any CPU.Build.0 = Debug|Any CPU {26FDEE3C-9D1F-79A6-F48F-D0944C7F09F8}.Release|Any CPU.ActiveCfg = Release|Any CPU {26FDEE3C-9D1F-79A6-F48F-D0944C7F09F8}.Release|Any CPU.Build.0 = Release|Any CPU + {2CB35142-AAD4-D424-61D3-88F9C94AD62A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2CB35142-AAD4-D424-61D3-88F9C94AD62A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2CB35142-AAD4-D424-61D3-88F9C94AD62A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2CB35142-AAD4-D424-61D3-88F9C94AD62A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE