Files
Terminal.Gui/Tests/UnitTestsParallelizable
Tig 726b15dd28 Fixes #4389 - Add comprehensive unit tests for WindowsKeyConverter (#4390)
* Add comprehensive unit tests for WindowsKeyConverter

- Implement 118 parallelizable unit tests for WindowsKeyConverter

- Cover ToKey and ToKeyInfo methods with full bidirectional testing

- Test basic characters, modifiers, special keys, function keys

- Test VK_PACKET Unicode/IME input

- Test OEM keys, NumPad keys, and lock states

- Include round-trip conversion tests

- All tests passing successfully

Fixes #4389

* Add test documenting VK_PACKET surrogate pair limitation

VK_PACKET sends surrogate pairs (e.g., emoji) as two separate events. Current WindowsKeyConverter processes each independently without combining. Test documents this limitation for future fix at InputProcessor level.

* Mark WindowsKeyConverterTests as Windows-only

Tests now skip on non-Windows platforms using SkipException in constructor. This prevents CI failures on macOS and Linux where Windows Console API is not available.

* Properly mark WindowsKeyConverter tests as Windows-only

Created custom xUnit attributes SkipOnNonWindowsFact and SkipOnNonWindowsTheory that automatically skip tests on non-Windows platforms. This prevents CI failures on macOS and Linux.

* Refactor and enhance test coverage for input processors

Refactored `ApplicationImpl.cs` to simplify its structure by removing the `_stopAfterFirstIteration` field. Reintroduced and modernized test files with improved structure and coverage:

- `NetInputProcessorTests.cs`: Added tests for `ConsoleKeyInfo` to `Rune`/`Key` conversion and queue processing.
- `WindowSizeMonitorTests.cs`: Added tests for size change event handling.
- `WindowsInputProcessorTests.cs`: Added tests for keyboard and mouse input processing, including mouse flag mapping.
- `WindowsKeyConverterTests.cs`: Added comprehensive tests for `InputRecord` to `Key` conversion, covering OEM keys, modifiers, Unicode, and round-trip integrity.

Improved test coverage for edge cases, introduced parameterized tests, and documented known limitations for future improvements.

* Use Trait-based platform filtering for WindowsKeyConverter tests

Added [Trait('Platform', 'Windows')] and [Collection('Global Test Setup')] attributes. Tests will run on Windows but can be filtered in CI on other platforms using --filter 'Platform!=Windows' if needed. This approach doesn't interfere with GlobalTestSetup and works correctly with xUnit.

* Filter Windows-specific tests on non-Windows CI platforms

Added --filter 'Platform!=Windows' for Linux and macOS runners to exclude WindowsKeyConverterTests which require Windows Console APIs. Windows runner runs all tests normally.

* Fix log path typo and remove Codecov upload step

Corrected a typo in the log directory path from
`logs/UnitTestsParallelable/` to `logs/UnitTestsParallelizable/`.
Removed the "Upload Parallelizable UnitTests Coverage to Codecov"
step, which was conditional on `matrix.os == 'ubuntu-latest'`
and used the `codecov/codecov-action@v4` action. This change
improves log handling and removes the Codecov integration.

* Refactor application reset logic

Replaced `Application.ResetState(true)` with a more explicit reset
mechanism. Introduced `ApplicationImpl.SetInstance(null)` to clear
the application instance and added `CM.Disable(true)` to disable
specific components. This change improves control over the reset
process and ensures a more granular approach to application state
management.

* Improve null safety with ?. and ! operators

Enhanced null safety across the codebase by introducing the null-conditional operator (`?.`) and null-forgiving operator (`!`) where appropriate.

- Updated `app` and `driver` method calls to use `?.` to prevent potential `NullReferenceException` errors.
- Added `!` to assert non-nullability in cases like `e.Value!.ToString()` and `app.Driver?.Contents!`.
- Modified `lv.SelectedItemChanged` event handler to ensure safe handling of nullable values.
- Updated `app.Shutdown()`, `app.LayoutAndDraw()`, and mouse event handling to use `?.`.
- Ensured `driver.SetScreenSize` is invoked only when `driver` is not null.
- Improved string concatenation logic with null-forgiving operator for `Contents`.
- General improvements to null safety to make the code more robust and resilient to null references.

* Improve Unicode tests and clarify surrogate pair handling

Updated `WindowsKeyConverterTests` to enhance readability, improve test data, and clarify comments. Key changes include:

- Reformatted `[Collection]` and `[Trait]` attributes for consistency.
- Replaced placeholder Unicode characters with meaningful examples:
  - Chinese: `中`, Japanese: `日`, Korean: `한`, Accented: `é`, Euro: `€`, Greek: `Ω`.
- Updated comments to replace placeholder emojis (`??`) with `😀` (U+1F600) for clarity.
- Adjusted surrogate pair test data to accurately reflect `😀`.
- Improved documentation of current limitations and future fixes for surrogate pair handling.

These changes ensure more accurate and meaningful test cases while improving code clarity.

* Ensure platform-specific test execution on Windows

Added `System.Runtime.InteropServices` and `Xunit.Sdk` namespaces to `WindowsKeyConverterTests.cs` to support platform checks and test setup. Marked the test class with `[Collection("Global Test Setup")]` to enable shared test setup. Updated the `ToKey_NumPadKeys_ReturnsExpectedKeyCode` method to include a platform check, ensuring the test only runs on Windows platforms.

* Integrate TrueColor support into ColorPicker

Merged TrueColors functionality into ColorPicker, enhancing the scenario with TrueColor demonstration and gradient features. Updated `ColorPicker.cs` to include driver information, TrueColor support indicators, and a toggle for `Force16Colors`. Removed `TrueColors.cs` as its functionality is now consolidated.

Refined `ColorBar` to use dynamic height with `Dim.Auto` for better flexibility. Added documentation to `HueBar` to clarify its role in representing the Hue component in HSL color space.

* Revert workflow change.

* Reverted attribute that didn't actualy work.
2025-11-20 13:20:01 -05:00
..

UnitTests.Parallelizable

This project contains unit tests that can run in parallel without interference. Tests here must not depend on global state or static Application infrastructure.

Migration Rules

Tests CAN be parallelized if they:

  • Test properties, constructors, and basic operations
  • Use [SetupFakeDriver] without Application statics
  • Call View.Draw(), LayoutAndDraw() without Application statics
  • Verify visual output with DriverAssert (when using [SetupFakeDriver])
  • Create View hierarchies without Application.Top
  • Test events and behavior without global state
  • Use View.BeginInit() / View.EndInit() for initialization

Tests CANNOT be parallelized if they:

  • Use [AutoInitShutdown] - requires Application.Init/Shutdown which creates global state
  • Set Application.Driver (global singleton)
  • Call Application.Init(), Application.Run/Run<T>(), or Application.Begin()
  • Modify ConfigurationManager global state (Enable/Load/Apply/Disable)
  • Access ConfigurationManager including ThemeManager and SchemeManager - these rely on global state
  • Access SchemeManager.GetSchemes() or dictionary lookups like schemes["Base"] - requires module initialization
  • Access View.Schemes - there can be weird interactions with xunit and dotnet module initialization such that tests run before module initialization sets up the Schemes array
  • Modify static properties like Key.Separator, CultureInfo.CurrentCulture, etc.
  • Set static members on View subclasses (e.g., configuration properties like Dialog.DefaultButtonAlignment) or any static fields/properties - these are shared across all parallel tests
  • Use Application.Top, Application.Driver, Application.MainLoop, or Application.Navigation
  • Are true integration tests that test multiple components working together

Important Notes

  • Many tests in UnitTests blindly use the above patterns when they don't actually need them
  • These tests CAN be rewritten to remove unnecessary dependencies and migrated here
  • Many tests APPEAR to be integration tests but are just poorly written and cover multiple surface areas - these can be split into focused unit tests
  • When in doubt, analyze if the test truly needs global state or can be refactored

How to Migrate Tests

  1. Identify tests in UnitTests that don't actually need Application statics
  2. Rewrite tests to remove [AutoInitShutdown], Application.Begin(), etc. if not needed
  3. Move the test to the equivalent file in UnitTests.Parallelizable
  4. Delete the old test from UnitTests to avoid duplicates
  5. Verify no duplicate test names exist (CI will check this)
  6. Test to ensure the migrated test passes

Example Migrations

Simple Property Test (no changes needed)

// Before (in UnitTests)
[Fact]
public void Constructor_Sets_Defaults ()
{
    var view = new Button ();
    Assert.Empty (view.Text);
}

// After (in UnitTests.Parallelizable) - just move it!
[Fact]
public void Constructor_Sets_Defaults ()
{
    var view = new Button ();
    Assert.Empty (view.Text);
}

Remove Unnecessary [SetupFakeDriver]

// Before (in UnitTests)
[Fact]
[SetupFakeDriver]
public void Event_Fires_When_Property_Changes ()
{
    var view = new Button ();
    var fired = false;
    view.TextChanged += (s, e) => fired = true;
    view.Text = "Hello";
    Assert.True (fired);
}

// After (in UnitTests.Parallelizable) - remove attribute!
[Fact]
public void Event_Fires_When_Property_Changes ()
{
    var view = new Button ();
    var fired = false;
    view.TextChanged += (s, e) => fired = true;
    view.Text = "Hello";
    Assert.True (fired);
}

Replace Application.Begin with View Initialization

// Before (in UnitTests)
[Fact]
[AutoInitShutdown]
public void Focus_Test ()
{
    var view = new Button ();
    var top = new Toplevel ();
    top.Add (view);
    Application.Begin (top);
    view.SetFocus ();
    Assert.True (view.HasFocus);
    top.Dispose ();
}

// After (in UnitTests.Parallelizable) - use BeginInit/EndInit!
[Fact]
public void Focus_Test ()
{
    var superView = new View ();
    var view = new Button ();
    superView.Add (view);
    superView.BeginInit ();
    superView.EndInit ();
    view.SetFocus ();
    Assert.True (view.HasFocus);
}

Running Tests

Tests in this project run in parallel automatically. To run them:

dotnet test Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj

See Also