mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Phase 2: Update examples with attributes and add test infrastructure
- Updated Example, FluentExample, and RunnableWrapperExample with example attributes - Added support for driver name detection from test context in examples - Created ExampleTests class in UnitTestsParallelizable with tests for: - Example metadata validation - Out-of-process execution - In-process execution - Context serialization - Examples now properly detect and use FakeDriver from test context - Tests pass for metadata validation and serialization Co-authored-by: tig <585482+tig@users.noreply.github.com>
This commit is contained in:
@@ -5,14 +5,31 @@
|
||||
|
||||
using Terminal.Gui.App;
|
||||
using Terminal.Gui.Configuration;
|
||||
using Terminal.Gui.Examples;
|
||||
using Terminal.Gui.ViewBase;
|
||||
using Terminal.Gui.Views;
|
||||
|
||||
[assembly: ExampleMetadata ("Simple Example", "A basic login form demonstrating Terminal.Gui fundamentals")]
|
||||
[assembly: ExampleCategory ("Getting Started")]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = new [] { "a", "d", "m", "i", "n", "Tab", "p", "a", "s", "s", "w", "o", "r", "d", "Enter" }, Order = 1)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = new [] { "Enter" }, DelayMs = 500, Order = 2)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = new [] { "Esc" }, DelayMs = 100, Order = 3)]
|
||||
|
||||
// Override the default configuration for the application to use the Light theme
|
||||
ConfigurationManager.RuntimeConfig = """{ "Theme": "Light" }""";
|
||||
ConfigurationManager.Enable (ConfigLocations.All);
|
||||
|
||||
IApplication app = Application.Create ();
|
||||
// Check for test context to determine driver
|
||||
string? contextJson = Environment.GetEnvironmentVariable (ExampleContext.EnvironmentVariableName);
|
||||
string? driverName = null;
|
||||
|
||||
if (!string.IsNullOrEmpty (contextJson))
|
||||
{
|
||||
ExampleContext? context = ExampleContext.FromJson (contextJson);
|
||||
driverName = context?.DriverName;
|
||||
}
|
||||
|
||||
IApplication app = Application.Create ().Init (driverName);
|
||||
|
||||
app.Run<ExampleWindow> ();
|
||||
|
||||
|
||||
@@ -2,11 +2,28 @@
|
||||
|
||||
using Terminal.Gui.App;
|
||||
using Terminal.Gui.Drawing;
|
||||
using Terminal.Gui.Examples;
|
||||
using Terminal.Gui.ViewBase;
|
||||
using Terminal.Gui.Views;
|
||||
|
||||
[assembly: ExampleMetadata ("Fluent API Example", "Demonstrates the fluent IApplication API with IRunnable pattern")]
|
||||
[assembly: ExampleCategory ("API Patterns")]
|
||||
[assembly: ExampleCategory ("Controls")]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = new [] { "CursorDown", "CursorDown", "CursorRight", "Enter" }, Order = 1)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = new [] { "Esc" }, DelayMs = 100, Order = 2)]
|
||||
|
||||
// Check for test context to determine driver
|
||||
string? contextJson = Environment.GetEnvironmentVariable (ExampleContext.EnvironmentVariableName);
|
||||
string? driverName = null;
|
||||
|
||||
if (!string.IsNullOrEmpty (contextJson))
|
||||
{
|
||||
ExampleContext? context = ExampleContext.FromJson (contextJson);
|
||||
driverName = context?.DriverName;
|
||||
}
|
||||
|
||||
IApplication? app = Application.Create ()
|
||||
.Init ()
|
||||
.Init (driverName)
|
||||
.Run<ColorPickerView> ();
|
||||
|
||||
// Run the application with fluent API - automatically creates, runs, and disposes the runnable
|
||||
|
||||
@@ -2,11 +2,31 @@
|
||||
|
||||
using Terminal.Gui.App;
|
||||
using Terminal.Gui.Drawing;
|
||||
using Terminal.Gui.Examples;
|
||||
using Terminal.Gui.ViewBase;
|
||||
using Terminal.Gui.Views;
|
||||
|
||||
[assembly: ExampleMetadata ("Runnable Wrapper Example", "Shows how to wrap any View to make it runnable without implementing IRunnable")]
|
||||
[assembly: ExampleCategory ("API Patterns")]
|
||||
[assembly: ExampleCategory ("Views")]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = new [] { "t", "e", "s", "t", "Esc" }, Order = 1)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = new [] { "Enter", "Esc" }, DelayMs = 100, Order = 2)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = new [] { "Enter", "Esc" }, DelayMs = 100, Order = 3)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = new [] { "Enter", "Esc" }, DelayMs = 100, Order = 4)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = new [] { "Enter", "Esc" }, DelayMs = 100, Order = 5)]
|
||||
|
||||
// Check for test context to determine driver
|
||||
string? contextJson = Environment.GetEnvironmentVariable (ExampleContext.EnvironmentVariableName);
|
||||
string? driverName = null;
|
||||
|
||||
if (!string.IsNullOrEmpty (contextJson))
|
||||
{
|
||||
ExampleContext? context = ExampleContext.FromJson (contextJson);
|
||||
driverName = context?.DriverName;
|
||||
}
|
||||
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ();
|
||||
app.Init (driverName);
|
||||
|
||||
// Example 1: Use extension method with result extraction
|
||||
var textField = new TextField { Width = 40, Text = "Default text" };
|
||||
|
||||
154
Tests/UnitTestsParallelizable/Examples/ExampleTests.cs
Normal file
154
Tests/UnitTestsParallelizable/Examples/ExampleTests.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
#nullable enable
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Terminal.Gui.Examples;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace UnitTests.Parallelizable.Examples;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the example discovery and execution infrastructure.
|
||||
/// </summary>
|
||||
public class ExampleTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public ExampleTests (ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Discovers all examples by looking for assemblies with ExampleMetadata attributes.
|
||||
/// </summary>
|
||||
/// <returns>Test data for all discovered examples.</returns>
|
||||
[RequiresUnreferencedCode ("Calls ExampleDiscovery.DiscoverFromDirectory")]
|
||||
[RequiresDynamicCode ("Calls ExampleDiscovery.DiscoverFromDirectory")]
|
||||
public static IEnumerable<object []> AllExamples ()
|
||||
{
|
||||
string examplesDir = Path.GetFullPath (Path.Combine (AppContext.BaseDirectory, "..", "..", "..", "..", "..", "Examples"));
|
||||
|
||||
if (!Directory.Exists (examplesDir))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
List<ExampleInfo> examples = ExampleDiscovery.DiscoverFromDirectory (examplesDir).ToList ();
|
||||
|
||||
if (examples.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
return examples.Select (e => new object [] { e });
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData (nameof (AllExamples))]
|
||||
public void Example_Has_Metadata (ExampleInfo example)
|
||||
{
|
||||
Assert.NotNull (example);
|
||||
Assert.False (string.IsNullOrWhiteSpace (example.Name), "Example name should not be empty");
|
||||
Assert.False (string.IsNullOrWhiteSpace (example.Description), "Example description should not be empty");
|
||||
Assert.True (File.Exists (example.AssemblyPath), $"Example assembly should exist: {example.AssemblyPath}");
|
||||
|
||||
_output.WriteLine ($"Example: {example.Name}");
|
||||
_output.WriteLine ($" Description: {example.Description}");
|
||||
_output.WriteLine ($" Categories: {string.Join (", ", example.Categories)}");
|
||||
_output.WriteLine ($" Assembly: {example.AssemblyPath}");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData (nameof (AllExamples))]
|
||||
public void All_Examples_Quit_And_Init_Shutdown_Properly_OutOfProcess (ExampleInfo example)
|
||||
{
|
||||
_output.WriteLine ($"Running example '{example.Name}' out-of-process");
|
||||
|
||||
ExampleContext context = new ()
|
||||
{
|
||||
DriverName = "FakeDriver",
|
||||
KeysToInject = new () { "Esc" },
|
||||
TimeoutMs = 5000,
|
||||
CollectMetrics = false,
|
||||
Mode = ExecutionMode.OutOfProcess
|
||||
};
|
||||
|
||||
ExampleResult result = ExampleRunner.Run (example, context);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
_output.WriteLine ($"Example failed: {result.ErrorMessage}");
|
||||
|
||||
if (!string.IsNullOrEmpty (result.StandardOutput))
|
||||
{
|
||||
_output.WriteLine ($"Standard Output:\n{result.StandardOutput}");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty (result.StandardError))
|
||||
{
|
||||
_output.WriteLine ($"Standard Error:\n{result.StandardError}");
|
||||
}
|
||||
}
|
||||
|
||||
Assert.True (result.Success, $"Example '{example.Name}' should complete successfully");
|
||||
Assert.False (result.TimedOut, $"Example '{example.Name}' should not timeout");
|
||||
Assert.Equal (0, result.ExitCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData (nameof (AllExamples))]
|
||||
public void All_Examples_Quit_And_Init_Shutdown_Properly_InProcess (ExampleInfo example)
|
||||
{
|
||||
_output.WriteLine ($"Running example '{example.Name}' in-process");
|
||||
|
||||
// Force a complete reset to ensure clean state
|
||||
Application.ResetState (true);
|
||||
|
||||
ExampleContext context = new ()
|
||||
{
|
||||
DriverName = "FakeDriver",
|
||||
KeysToInject = new () { "Esc" },
|
||||
TimeoutMs = 5000,
|
||||
CollectMetrics = false,
|
||||
Mode = ExecutionMode.InProcess
|
||||
};
|
||||
|
||||
ExampleResult result = ExampleRunner.Run (example, context);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
_output.WriteLine ($"Example failed: {result.ErrorMessage}");
|
||||
}
|
||||
|
||||
// Reset state after in-process execution
|
||||
Application.ResetState (true);
|
||||
|
||||
Assert.True (result.Success, $"Example '{example.Name}' should complete successfully");
|
||||
Assert.False (result.TimedOut, $"Example '{example.Name}' should not timeout");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExampleContext_Serialization_Works ()
|
||||
{
|
||||
ExampleContext context = new ()
|
||||
{
|
||||
DriverName = "FakeDriver",
|
||||
KeysToInject = new () { "Esc", "Enter" },
|
||||
TimeoutMs = 5000,
|
||||
MaxIterations = 100,
|
||||
CollectMetrics = true,
|
||||
Mode = ExecutionMode.InProcess
|
||||
};
|
||||
|
||||
string json = context.ToJson ();
|
||||
Assert.False (string.IsNullOrWhiteSpace (json));
|
||||
|
||||
ExampleContext? deserialized = ExampleContext.FromJson (json);
|
||||
Assert.NotNull (deserialized);
|
||||
Assert.Equal (context.DriverName, deserialized.DriverName);
|
||||
Assert.Equal (context.TimeoutMs, deserialized.TimeoutMs);
|
||||
Assert.Equal (context.MaxIterations, deserialized.MaxIterations);
|
||||
Assert.Equal (context.CollectMetrics, deserialized.CollectMetrics);
|
||||
Assert.Equal (context.Mode, deserialized.Mode);
|
||||
Assert.Equal (context.KeysToInject.Count, deserialized.KeysToInject.Count);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user