diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeConsole.cs b/Terminal.Gui/Drivers/FakeDriver/FakeConsole.cs index 9eb566768..58b58c590 100644 --- a/Terminal.Gui/Drivers/FakeDriver/FakeConsole.cs +++ b/Terminal.Gui/Drivers/FakeDriver/FakeConsole.cs @@ -5,7 +5,48 @@ namespace Terminal.Gui.Drivers; #pragma warning disable RCS1138 // Add summary to documentation comment. -/// +/// +/// Static mock console implementation that simulates the .NET Console API for testing purposes. +/// Used by to provide input/output simulation without requiring a real terminal. +/// +/// +/// +/// provides static properties and methods that mirror the standard +/// API. It maintains internal state for window size, cursor position, +/// colors, and buffered input/output. +/// +/// +/// Key Capabilities: +/// +/// +/// Simulating keyboard input via +/// Tracking cursor position and visibility +/// Managing console colors (foreground/background) +/// Simulating window and buffer size operations +/// Recording output for verification +/// +/// +/// Thread Safety: This class maintains static state and is not thread-safe. +/// Tests should not run in parallel if they access FakeConsole. +/// +/// +/// Usage: Most tests don't need to interact with FakeConsole directly. +/// uses it internally, and +/// handles setup/teardown. However, tests can use to +/// simulate keyboard input. +/// +/// +/// +/// +/// // Simulate keyboard input +/// FakeConsole.PushMockKeyPress(new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); +/// FakeConsole.PushMockKeyPress(new ConsoleKeyInfo('\r', ConsoleKey.Enter, false, false, false)); +/// +/// // The input will be processed by FakeDriver when Application.Run or RunIteration is called +/// +/// +/// +/// public static class FakeConsole { #pragma warning restore RCS1138 // Add summary to documentation comment. diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs index 34f463a8f..08ac40eea 100644 --- a/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs @@ -10,7 +10,41 @@ using System.Runtime.InteropServices; namespace Terminal.Gui.Drivers; -/// Implements a mock IConsoleDriver for unit testing +/// +/// Implements a mock for unit testing. This driver simulates console behavior +/// without requiring a real terminal, allowing for deterministic testing of Terminal.Gui applications. +/// +/// +/// +/// extends the legacy base class and is designed +/// for backward compatibility with existing tests. It provides programmatic control over console state, +/// including screen size, keyboard input, and output verification. +/// +/// +/// Key Features: +/// +/// +/// Programmatic screen resizing via and +/// Keyboard input simulation via +/// Mouse input simulation via methods +/// Output verification via buffer inspection +/// Event firing for resize, keyboard, and mouse events +/// +/// +/// Usage: Most tests should use which automatically +/// initializes Application with FakeDriver. For more control, create and configure FakeDriver instances directly. +/// +/// +/// Thread Safety: FakeDriver is not thread-safe. Tests using this driver should not run +/// in parallel with other tests that access driver state. +/// +/// +/// For detailed usage examples and patterns, see the README.md file in this directory. +/// +/// +/// +/// +/// public class FakeDriver : ConsoleDriver { #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member diff --git a/Terminal.Gui/Drivers/FakeDriver/README.md b/Terminal.Gui/Drivers/FakeDriver/README.md new file mode 100644 index 000000000..fe99f4356 --- /dev/null +++ b/Terminal.Gui/Drivers/FakeDriver/README.md @@ -0,0 +1,205 @@ +# FakeDriver Testing Guide + +## Overview + +Terminal.Gui provides testing infrastructure through two complementary driver implementations: + +1. **FakeDriver** (this directory) - Legacy ConsoleDriver-based fake driver for backward compatibility +2. **FakeConsoleDriver** (in TerminalGuiFluentTesting) - Modern component-based fake driver for fluent testing + +## FakeDriver Architecture + +`FakeDriver` extends the abstract `ConsoleDriver` base class and is designed for: +- Backward compatibility with existing tests +- Use with `AutoInitShutdownAttribute` in traditional unit tests +- Direct driver manipulation and state inspection + +### Key Components + +- **FakeDriver.cs** - Main driver implementation +- **FakeConsole.cs** - Static mock console for input/output simulation +- **FakeComponentFactory.cs** - Component factory for modern initialization path +- **FakeConsoleInput.cs** - Input simulation +- **FakeConsoleOutput.cs** - Output capture and validation +- **FakeWindowSizeMonitor.cs** - Window resize simulation + +## Usage Patterns + +### Basic Test Setup + +```csharp +[Fact] +[AutoInitShutdown] // Automatically initializes and shuts down Application +public void My_Test() +{ + // Application is already initialized with FakeDriver + Assert.NotNull(Application.Driver); + Assert.True(Application.Initialized); +} +``` + +### Simulating Screen Resizes + +```csharp +[Fact] +[AutoInitShutdown] +public void Test_Resize_Behavior() +{ + // Start with default size (80x25) + Assert.Equal(80, Application.Driver.Cols); + Assert.Equal(25, Application.Driver.Rows); + + // Simulate a terminal resize + AutoInitShutdownAttribute.FakeResize(new Size(120, 40)); + + // Verify the resize took effect + Assert.Equal(120, Application.Driver.Cols); + Assert.Equal(40, Application.Driver.Rows); + Assert.Equal(new Rectangle(0, 0, 120, 40), Application.Screen); +} +``` + +### Subscribing to Resize Events + +```csharp +[Fact] +[AutoInitShutdown] +public void Test_Resize_Events() +{ + bool eventFired = false; + Size? newSize = null; + + Application.Driver.SizeChanged += (sender, args) => + { + eventFired = true; + newSize = args.Size; + }; + + AutoInitShutdownAttribute.FakeResize(new Size(100, 30)); + + Assert.True(eventFired); + Assert.Equal(100, newSize.Value.Width); + Assert.Equal(30, newSize.Value.Height); +} +``` + +### Simulating Keyboard Input + +```csharp +[Fact] +[AutoInitShutdown] +public void Test_Keyboard_Input() +{ + // Queue keyboard input before it's processed + FakeConsole.PushMockKeyPress(new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); + FakeConsole.PushMockKeyPress(new ConsoleKeyInfo('\r', ConsoleKey.Enter, false, false, false)); + + // Your test logic that processes the input + // ... +} +``` + +### Verifying Screen Output + +```csharp +[Fact] +[AutoInitShutdown] +public void Test_Screen_Output() +{ + Application.Top = new Toplevel(); + var label = new Label { Text = "Hello", X = 0, Y = 0 }; + Application.Top.Add(label); + + Application.Begin(Application.Top); + AutoInitShutdownAttribute.RunIteration(); + + // Access driver contents to verify output + var contents = Application.Driver.Contents; + Assert.NotNull(contents); + + // Verify specific characters were drawn + // contents[row, col] gives you access to individual cells +} +``` + +## Relationship Between Driver Properties + +Understanding the relationship between key driver properties: + +- **Screen** - `Rectangle` representing the full console area. Always starts at (0,0) with size (Cols, Rows). +- **Cols** - Number of columns (width) in the console. +- **Rows** - Number of rows (height) in the console. +- **Contents** - 2D array `[rows, cols]` containing the actual screen buffer cells. + +When you resize: +1. `SetBufferSize(width, height)` or `FakeResize(Size)` is called +2. `Cols` and `Rows` are updated +3. `Contents` buffer is reallocated to match new dimensions +4. `Screen` property returns updated rectangle +5. `SizeChanged` event fires with new size +6. Application propagates resize to top-level views + +## Thread Safety + +⚠️ **Important**: FakeDriver is **not thread-safe**. Tests that use FakeDriver should: +- Not run in parallel with other tests that access the driver +- Not share driver state between test methods +- Use `AutoInitShutdownAttribute` to ensure clean initialization/shutdown per test + +For parallel-safe tests, use the UnitTestsParallelizable project with its own test infrastructure. + +## Differences from Production Drivers + +FakeDriver differs from production drivers (WindowsDriver, UnixDriver, DotNetDriver) in several ways: + +| Aspect | FakeDriver | Production Drivers | +|--------|------------|-------------------| +| Screen Size | Programmatically set via `SetBufferSize` | Determined by actual terminal size | +| Input | Queued via `FakeConsole.PushMockKeyPress` | Reads from actual stdin | +| Output | Captured in memory buffer | Written to actual terminal | +| Resize | Triggered by test code | Triggered by OS (SIGWINCH, WINDOW_BUFFER_SIZE_EVENT) | +| Buffer vs Window | Always equal (no scrollback) | Can differ (scrollback support) | + +## Advanced: Direct Driver Access + +For tests that need more control, you can access the driver directly: + +```csharp +[Fact] +public void Test_With_Direct_Driver_Access() +{ + // Create and initialize driver manually + Application.ResetState(true); + var driver = new FakeDriver(); + Application.Driver = driver; + Application.SubscribeDriverEvents(); + + // Use driver directly + driver.SetBufferSize(100, 50); + + Assert.Equal(100, driver.Cols); + Assert.Equal(50, driver.Rows); + + // Cleanup + Application.ResetState(true); +} +``` + +## Modern Component-Based Architecture + +For new test infrastructure, consider using the modern component factory approach via `FakeComponentFactory`: + +```csharp +var factory = new FakeComponentFactory(); +// Modern driver initialization through component factory pattern +// This is used internally by the fluent testing infrastructure +``` + +The fluent testing project (`TerminalGuiFluentTesting`) provides a higher-level API built on this architecture. + +## See Also + +- **AutoInitShutdownAttribute** - Attribute for automatic test setup/teardown +- **TerminalGuiFluentTesting** - Modern fluent testing infrastructure +- **FakeConsole** - Static mock console used by FakeDriver +- **ConsoleDriver** - Base class documentation for all drivers