README.md 6.3 KB

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

[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

[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

[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

[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

[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:

[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:

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