Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
eaf540d
Initial plan
Copilot Mar 22, 2026
7b7d49b
Refactor tests: rename UnitTests to UnitTests.Legacy, add UnitTests.N…
Copilot Mar 22, 2026
b940474
Fix IntegrationTests.csproj: update project reference from UnitTests …
Copilot Mar 22, 2026
b6d58d6
Step 4 iteration 1: delete 37 Legacy test files already covered in Un…
Copilot Mar 22, 2026
5e45e73
Step 4 iteration 2: delete 14 more Legacy test files covered in UnitT…
Copilot Mar 22, 2026
14b2779
Step 4 iteration 3: delete 5 more Legacy test files (Config and TextV…
Copilot Mar 22, 2026
e8be9b4
Step 4 iteration 4: delete UICatalog tests, move ConfigurationManager…
Copilot Mar 22, 2026
6ddab1b
Step 4 iteration 5: delete FrameViewTests (covered incidentally in Para)
Copilot Mar 22, 2026
92abe47
Step 4 iteration 6: delete StatusBarTests (covered by BarTests in Para)
Copilot Mar 22, 2026
18ea1bb
Fix code style: remove trailing whitespace and extra blank lines in N…
Copilot Mar 22, 2026
095c357
Step 4 iteration 7: SpinnerViewTests - add minimal Para tests, delete…
Copilot Mar 22, 2026
9ed6816
Step 4 iteration 8: TreeTableSourceTests - add 4 minimal Para tests, …
Copilot Mar 22, 2026
e28086f
Step 4 iteration 9: SynchronizatonContextTests - add 2 minimal NonPar…
Copilot Mar 22, 2026
730cce2
Step 4 iteration 10: FileSystemCollectionNavigationMatcher tests - ad…
Copilot Mar 22, 2026
3fe7b88
Step 4 iteration 11: delete TabViewTests from Legacy (TabView being r…
Copilot Mar 22, 2026
c82e0ca
Step 4 iteration 12: AppendAutocompleteTests - add 4 minimal Para tes…
Copilot Mar 22, 2026
f5c5866
Step 4 iteration 13: ProgressBarTests - add 4 minimal Para tests, del…
Copilot Mar 22, 2026
799f338
Step 4 iteration 14: FileDialogTests - add 13 AllowedType Para tests,…
Copilot Mar 22, 2026
37cdde7
Step 4 iteration 15: GraphViewTests - add 8 minimal Para tests, delet…
Copilot Mar 22, 2026
282d1ab
Step 4 iteration 16: TableViewTests - add 15 minimal Para tests, dele…
Copilot Mar 22, 2026
3863646
Fix CI: remove UnitTests.Legacy step from workflow (project now has 0…
Copilot Mar 22, 2026
5c4d871
Update SynchronizationContextTests.cs
tig Mar 23, 2026
86ee3e3
Update UnitTests.NonParallelizable.csproj
tig Mar 23, 2026
8eea300
Update unit-tests.yml
tig Mar 23, 2026
0323675
Update ConfigurationMangerTests.cs
tig Mar 23, 2026
a5e953d
Fix SynchronizationContextTests: use modern instance model (Applicati…
Copilot Mar 23, 2026
36dad4e
Merge remote-tracking branch 'origin/v2_develop' into copilot/refacto…
Copilot Mar 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ The repository uses multiple GitHub Actions workflows. What runs and when:
1. Each OS checks out code, restores, and builds locally
2. **Performance optimizations**:
- Disables Windows Defender on Windows runners (significant speedup)
3. Runs two test jobs:
- **Non-parallel UnitTests**: `Tests/UnitTests` with diagnostic output
3. Runs three test jobs:
- **Non-parallel UnitTests.Legacy**: `Tests/UnitTests.Legacy` with diagnostic output
- **Non-parallel UnitTests.NonParallelizable**: `Tests/UnitTests.NonParallelizable` with diagnostic output
- **Parallel UnitTestsParallelizable**: `Tests/UnitTestsParallelizable` with diagnostic output
Comment thread
tig marked this conversation as resolved.
4. Uploads test logs and diagnostic data from all runners

Expand Down Expand Up @@ -87,7 +88,8 @@ The repository uses multiple GitHub Actions workflows. What runs and when:
# Full CI sequence:
dotnet restore
dotnet build --configuration Debug --no-restore
dotnet test --project Tests/UnitTests --no-build --verbosity normal
dotnet test --project Tests/UnitTests.Legacy --no-build --verbosity normal
dotnet test --project Tests/UnitTests.NonParallelizable --no-build --verbosity normal
dotnet test --project Tests/UnitTestsParallelizable --no-build --verbosity normal
dotnet build --configuration Release --no-restore
```
10 changes: 5 additions & 5 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,23 @@ jobs:
Add-MpPreference -ExclusionPath "${{ github.workspace }}"
Add-MpPreference -ExclusionProcess "dotnet.exe"

- name: Run UnitTests
- name: Run UnitTests.NonParallelizable
shell: bash
run: |
dotnet test \
--project Tests/UnitTests \
--project Tests/UnitTests.NonParallelizable \
--no-build \
--verbosity normal \
--diagnostic --diagnostic-output-directory logs/UnitTests/${{ runner.os }}
--diagnostic --diagnostic-output-directory logs/UnitTests.NonParallelizable/${{ runner.os }}

- name: Upload Test Logs
if: always()
uses: actions/upload-artifact@v4
with:
name: non_parallel_unittests-logs-${{ runner.os }}
path: |
logs/UnitTests/
Tests/UnitTests/logs/
logs/UnitTests.NonParallelizable/
Tests/UnitTests.NonParallelizable/logs/
TestResults/
**/*.dmp
if-no-files-found: ignore
Expand Down
2 changes: 2 additions & 0 deletions Terminal.Gui/Terminal.Gui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@
<ItemGroup>
<InternalsVisibleTo Include="Benchmarks" />
<InternalsVisibleTo Include="UnitTests" />
<InternalsVisibleTo Include="UnitTests.Legacy" />
<InternalsVisibleTo Include="UnitTests.NonParallelizable" />
<InternalsVisibleTo Include="UnitTests.Parallelizable" />
<InternalsVisibleTo Include="StressTests" />
<InternalsVisibleTo Include="IntegrationTests" />
Expand Down
11 changes: 9 additions & 2 deletions Terminal.sln
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeAot", "Examples\Nativ
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Tests\Benchmarks\Benchmarks.csproj", "{242FBD3E-2EC6-4274-BD40-8E62AF9327B2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "Tests\UnitTests\UnitTests.csproj", "{038B09F5-EF3A-F21E-7C10-A6551866ECE2}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests.Legacy", "Tests\UnitTests.Legacy\UnitTests.Legacy.csproj", "{038B09F5-EF3A-F21E-7C10-A6551866ECE2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests.NonParallelizable", "Tests\UnitTests.NonParallelizable\UnitTests.NonParallelizable.csproj", "{B7C94F2A-3E8D-4A1B-9F5C-2D1E6A3B7C90}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "Tests\IntegrationTests\IntegrationTests.csproj", "{F74EC349-B988-FCFA-A1E5-967F70FB75B5}"
EndProject
Expand All @@ -123,7 +125,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{1A3C
ProjectSection(SolutionItems) = preProject
codecov.yml = codecov.yml
Scripts\Run-LocalCoverage.ps1 = Scripts\Run-LocalCoverage.ps1
Tests\UnitTests\runsettings.xml = Tests\UnitTests\runsettings.xml
Tests\UnitTests.Legacy\runsettings.xml = Tests\UnitTests.Legacy\runsettings.xml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentExample", "Examples\FluentExample\FluentExample.csproj", "{8C05292F-86C9-C29A-635B-A4DFC5955D1C}"
Expand Down Expand Up @@ -189,6 +191,10 @@ Global
{038B09F5-EF3A-F21E-7C10-A6551866ECE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{038B09F5-EF3A-F21E-7C10-A6551866ECE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{038B09F5-EF3A-F21E-7C10-A6551866ECE2}.Release|Any CPU.Build.0 = Release|Any CPU
{B7C94F2A-3E8D-4A1B-9F5C-2D1E6A3B7C90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7C94F2A-3E8D-4A1B-9F5C-2D1E6A3B7C90}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7C94F2A-3E8D-4A1B-9F5C-2D1E6A3B7C90}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7C94F2A-3E8D-4A1B-9F5C-2D1E6A3B7C90}.Release|Any CPU.Build.0 = Release|Any CPU
{F74EC349-B988-FCFA-A1E5-967F70FB75B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F74EC349-B988-FCFA-A1E5-967F70FB75B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F74EC349-B988-FCFA-A1E5-967F70FB75B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -248,6 +254,7 @@ Global
{E6D716C6-AC94-4150-B10A-44AE13F79344} = {3DD033C0-E023-47BF-A808-9CCE30873C3E}
{242FBD3E-2EC6-4274-BD40-8E62AF9327B2} = {A589126F-C71A-4FEE-B7EA-2DCA1ADF6A46}
{038B09F5-EF3A-F21E-7C10-A6551866ECE2} = {A589126F-C71A-4FEE-B7EA-2DCA1ADF6A46}
{B7C94F2A-3E8D-4A1B-9F5C-2D1E6A3B7C90} = {A589126F-C71A-4FEE-B7EA-2DCA1ADF6A46}
{F74EC349-B988-FCFA-A1E5-967F70FB75B5} = {A589126F-C71A-4FEE-B7EA-2DCA1ADF6A46}
{96ACE8BA-2E07-7537-FBF2-E8176CCB8080} = {A589126F-C71A-4FEE-B7EA-2DCA1ADF6A46}
{DE780834-190A-8277-51FD-750CC666E82D} = {A589126F-C71A-4FEE-B7EA-2DCA1ADF6A46}
Expand Down
2 changes: 1 addition & 1 deletion Tests/IntegrationTests/IntegrationTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<ProjectReference Include="..\..\Tests\AppTestHelpers.XunitHelpers\AppTestHelpers.XunitHelpers.csproj" />
<ProjectReference Include="..\..\Tests\AppTestHelpers\AppTestHelpers.csproj" />
<ProjectReference Include="..\..\Examples\UICatalog\UICatalog.csproj" />
<ProjectReference Include="..\UnitTests\UnitTests.csproj" />
<ProjectReference Include="..\UnitTests.Legacy\UnitTests.Legacy.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
Expand Down
86 changes: 74 additions & 12 deletions Tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,90 @@

This folder contains the tests for Terminal.Gui.

## ./UnitTests
## Test Projects

This folder contains the unit tests for Terminal.Gui that can not be run in parallel. This is because they
depend on `Application` or other class that use static state or `ConfigurationManager`.
### ./UnitTestsParallelizable

We should be striving to move as many tests as possible to the `./UnitTestsParallelizable` folder.
The primary home for new tests. Tests here run in parallel (`parallelizeAssembly: true`, `maxParallelThreads: 12`)
and **must not** touch any process-wide static state.

## ./UnitTestsParallelizable
### ./UnitTests.NonParallelizable

This folder contains the unit tests for Terminal.Gui that can be run in parallel.
Tests that either explicitly test process-wide static state, or must set process-wide statics, or otherwise
cannot be run concurrently with other tests. These tests run with `parallelizeAssembly: false`.

## ./IntegrationTests
### ./UnitTests.Legacy

This folder contains the integration tests for Terminal.Gui.
Tests that have not yet been ported to `UnitTestsParallelizable` or `UnitTests.NonParallelizable`.
These tests are candidates for rewrite (if the case is not covered elsewhere) or deletion (if already covered).
Do not add new tests here.

## ./StressTests
### ./IntegrationTests

This folder contains the stress tests for Terminal.Gui.
Integration tests for Terminal.Gui.

## ./PerformanceTests
### ./StressTests

This folder WILL contain the performance tests for Terminal.Gui.
Stress tests for Terminal.Gui.

---

## Static State in Terminal.Gui

The following classes and properties use process-wide static state. Tests that read or write these **must not**
go in `UnitTestsParallelizable` — they belong in `UnitTests.NonParallelizable`.

### `Application` (legacy static façade — `Terminal.Gui.App/Legacy/`)

| Member | Notes |
|--------|-------|
| `Application.Instance` | Returns `ApplicationImpl.Instance` (singleton) |
| `Application.Init(driverName)` | Initializes the singleton; sets `Initialized`, `Driver`, `MainThreadId`, `SynchronizationContext` |
| `Application.Shutdown()` | Disposes the singleton; resets all static fields |
| `Application.Initialized` | Global bool; true between `Init` and `Shutdown` |
| `Application.MainThreadId` | Process-wide main thread ID set by `Init` |
| `Application.Driver` | The active `IDriver` singleton |
| `Application.DefaultKeyBindings` | Process-wide default key-binding dictionary (mutable `IDictionary`) |
| `Application.ForceDriver` | Persists across tests unless reset |
| `Application.Keyboard` | Delegates to `ApplicationImpl.Instance.Keyboard` |
| `Application.Navigation` | Delegates to `ApplicationImpl.Instance.Navigation` |
| `Application.Popovers` | Delegates to `ApplicationImpl.Instance.Popovers` |
| `Application.Screen` | Delegates to `ApplicationImpl.Instance.Screen` |
| `Application.ResetState()` | Resets all static backing fields to defaults |
| `SynchronizationContext.Current` | Set by `Application.Init`; process-wide |

### `ApplicationImpl` (singleton — `Terminal.Gui.App/ApplicationImpl.cs`)

| Member | Notes |
|--------|-------|
| `ApplicationImpl.Instance` | Process-wide singleton; set by `SetInstance()` |
| `ApplicationImpl.SetInstance(impl)` | Replaces the singleton (used in tests) |
| `ApplicationImpl.ResetModelUsageTracking()` | Resets the static "which model was used" flag |

### `ConfigurationManager` (singleton — `Terminal.Gui.Configuration/`)

| Member | Notes |
|--------|-------|
| `CM.IsEnabled` | Global flag; affects all code reading configuration |
| `CM.Disable(reset)` | Disables and optionally resets to hard-coded defaults |

### `View.Diagnostics` (static field — `Terminal.Gui.ViewBase/View.cs`)

| Member | Notes |
|--------|-------|
| `View.Diagnostics` | `ViewDiagnosticFlags`; process-wide debug flags |

---

## Which Project Does My Test Belong In?

| Test characteristic | Project |
|---------------------|---------|
| No static state; no `Application.Init`/`Shutdown`; no `ConfigurationManager` mutations | `UnitTestsParallelizable` |
| Calls `Application.Init`/`Shutdown` directly, or mutates `Application.DefaultKeyBindings`, `Application.ForceDriver`, or `ApplicationImpl.ResetModelUsageTracking()` | `UnitTests.NonParallelizable` |
| Uses `[AutoInitShutdown]` or `[SetupFakeApplication]` (fake driver, does not call real `Application.Init`) | Currently in `UnitTests.Legacy`; migrate to `UnitTestsParallelizable` or `UnitTests.NonParallelizable` after review |
| Not yet reviewed / ported | `UnitTests.Legacy` |

---

See the [Testing wiki](https://github.com/gui-cs/Terminal.Gui/wiki/Testing) for details on how to add more tests.
File renamed without changes.
File renamed without changes.
File renamed without changes.
13 changes: 13 additions & 0 deletions Tests/UnitTests.Legacy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# UnitTests.Legacy

This project contains tests that have not yet been ported to `UnitTestsParallelizable` or `UnitTests.NonParallelizable`.

**Do not add new tests here.** New tests should go in:
- [`../UnitTestsParallelizable`](../UnitTestsParallelizable/README.md) for tests with no static state dependency.
- [`../UnitTests.NonParallelizable`](../UnitTests.NonParallelizable/README.md) for tests that explicitly depend on process-wide static state.

Each test class in this project is a candidate for one of:
1. **Rewrite** in the appropriate project if the case is not already covered.
2. **Deletion** if the case is already covered in `UnitTestsParallelizable`.

See the [Testing wiki](https://github.com/gui-cs/Terminal.Gui/wiki/Testing) for details.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<InformationalVersion>2.0</InformationalVersion>
</PropertyGroup>
<PropertyGroup>
<AssemblyName>UnitTests.Legacy</AssemblyName>
<OutputType>Exe</OutputType>
<UseAppHost>true</UseAppHost>
<IsTestProject>true</IsTestProject>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "UnitTests.csproj", "{A29633F2-B26E-48B2-997A-1733286E3C13}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests.Legacy", "UnitTests.Legacy.csproj", "{A29633F2-B26E-48B2-997A-1733286E3C13}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace UnitTests.ApplicationTests;
namespace UnitTests.NonParallelizable.ApplicationTests;

/// <summary>
/// Tests to ensure that mixing legacy static Application and modern instance-based models
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#nullable enable
// ReSharper disable AccessToDisposedClosure

namespace UnitTests.ApplicationTests.Keyboard;
namespace UnitTests.NonParallelizable.ApplicationTests.Keyboard;

/// <summary>
/// Tests to verify that ApplicationKeyboard is thread-safe for concurrent access scenarios.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Tests that mutate Application.DefaultKeyBindings (static state).
// These MUST NOT be in UnitTestsParallelizable.

namespace UnitTests.ApplicationTests.Keyboard;
namespace UnitTests.NonParallelizable.ApplicationTests.Keyboard;

public class KeyboardSetterTests
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copilot
#nullable enable

namespace UnitTests.NonParallelizable.ApplicationTests;

/// <summary>
/// Tests for the <see cref="SynchronizationContext"/> that is set during the Terminal.Gui application lifecycle.
/// Must run non-concurrently because <see cref="IApplication.Init"/> and <see cref="IDisposable.Dispose"/> mutate
/// the process-wide <see cref="SynchronizationContext.Current"/>.
/// </summary>
public class SynchronizationContextTests
{
[Fact]
public void Init_SetsSynchronizationContext_Dispose_ClearsIt ()
{
IApplication app = Application.Create ();

try
{
app.Init (DriverRegistry.Names.ANSI);

Assert.NotNull (SynchronizationContext.Current);
}
finally
{
app.Dispose ();
}

Assert.Null (SynchronizationContext.Current);
}

[Fact]
public void Init_SynchronizationContext_CreateCopy_ReturnsDifferentInstance ()
{
IApplication app = Application.Create ();

try
{
app.Init (DriverRegistry.Names.ANSI);

SynchronizationContext context = SynchronizationContext.Current!;
SynchronizationContext copy = context.CreateCopy ();

Assert.NotNull (copy);
Assert.NotSame (context, copy);
}
finally
{
app.Dispose ();
}
}
}
15 changes: 15 additions & 0 deletions Tests/UnitTests.NonParallelizable/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
global using Attribute = Terminal.Gui.Drawing.Attribute;
global using Color = Terminal.Gui.Drawing.Color;
global using CM = Terminal.Gui.Configuration.ConfigurationManager;
global using Terminal.Gui.App;
global using Terminal.Gui.Time;
global using Terminal.Gui.Testing;
global using Terminal.Gui.Drivers;
global using Terminal.Gui.Input;
global using Terminal.Gui.Configuration;
global using Terminal.Gui.ViewBase;
global using Terminal.Gui.Views;
global using Terminal.Gui.Drawing;
global using Terminal.Gui.Text;
global using Terminal.Gui.Resources;
global using Terminal.Gui.FileServices;
Loading
Loading