diff --git a/.claude/rules/testing-patterns.md b/.claude/rules/testing-patterns.md
index d46e7c228f..990cb695fa 100644
--- a/.claude/rules/testing-patterns.md
+++ b/.claude/rules/testing-patterns.md
@@ -7,11 +7,8 @@
- **Never decrease code coverage** - PRs must maintain or increase coverage
- Target: **70%+ coverage** for new code
- Coverage collection:
- - Centralized in `TestResults/` directory at repository root
- - Collected only on Linux (ubuntu-latest) runners in CI for performance
- - Windows and macOS runners skip coverage collection to reduce execution time
- - Coverage reports uploaded to Codecov automatically from Linux runner
- - CI monitors coverage on each PR
+ - Temporarily disabled in CI during xUnit v3 / MTP migration
+ - Will be re-enabled once an MTP-compatible coverage solution is integrated
## Test Patterns
@@ -47,11 +44,11 @@
- ~10 min timeout
- Uses `Application.Init` and static state
- Cannot run in parallel
-- Includes `--blame` flags for crash diagnostics
+- Includes `--diagnostic` flag for logging
**Command:**
```bash
-dotnet test Tests/UnitTests --no-build --verbosity normal
+dotnet test --project Tests/UnitTests --no-build --verbosity normal
```
### 2. Parallel Tests (`Tests/UnitTestsParallelizable/`) - **PREFERRED**
@@ -67,7 +64,7 @@ dotnet test Tests/UnitTests --no-build --verbosity normal
**Command:**
```bash
-dotnet test Tests/UnitTestsParallelizable --no-build --verbosity normal
+dotnet test --project Tests/UnitTestsParallelizable --no-build --verbosity normal
```
### 3. Integration Tests (`Tests/IntegrationTests/`)
@@ -78,13 +75,13 @@ dotnet test Tests/UnitTestsParallelizable --no-build --verbosity normal
**Command:**
```bash
-dotnet test Tests/IntegrationTests --no-build --verbosity normal
+dotnet test --project Tests/IntegrationTests --no-build --verbosity normal
```
## Test Configuration Files
- `xunit.runner.json` - xUnit configuration
-- `coverlet.runsettings` - Coverage settings (OpenCover format)
+- `coverlet.runsettings` - Coverage settings (currently unused, pending MTP integration)
## Example Test Pattern
diff --git a/.claude/tasks/clean-code-review.md b/.claude/tasks/clean-code-review.md
index 0c814ca006..30ded9e58e 100644
--- a/.claude/tasks/clean-code-review.md
+++ b/.claude/tasks/clean-code-review.md
@@ -50,9 +50,9 @@ Run these for each commit:
```bash
dotnet build --no-restore
-dotnet test Tests/IntegrationTests --no-build
-dotnet test Tests/UnitTests --no-build
-dotnet test Tests/UnitTestsParallelizable --no-build
+dotnet test --project Tests/IntegrationTests --no-build
+dotnet test --project Tests/UnitTests --no-build
+dotnet test --project Tests/UnitTestsParallelizable --no-build
```
## Terminal.Gui Specific Requirements
diff --git a/.claude/workflows/build-test-workflow.md b/.claude/workflows/build-test-workflow.md
index e86fa5e516..36b1a0ed2b 100644
--- a/.claude/workflows/build-test-workflow.md
+++ b/.claude/workflows/build-test-workflow.md
@@ -47,19 +47,19 @@ dotnet build --configuration Release --no-restore
**Time:** ~10 min timeout
```bash
-dotnet test Tests/UnitTests --no-build --verbosity normal
+dotnet test --project Tests/UnitTests --no-build --verbosity normal
```
- Uses `Application.Init` and static state
- Cannot run in parallel
-- Includes `--blame` flags for crash diagnostics
+- Includes `--diagnostic` flag for logging
### Run Parallel Tests (Preferred)
**Time:** ~10 min timeout
```bash
-dotnet test Tests/UnitTestsParallelizable --no-build --verbosity normal
+dotnet test --project Tests/UnitTestsParallelizable --no-build --verbosity normal
```
- No dependencies on static state
@@ -69,13 +69,13 @@ dotnet test Tests/UnitTestsParallelizable --no-build --verbosity normal
### Run Integration Tests
```bash
-dotnet test Tests/IntegrationTests --no-build --verbosity normal
+dotnet test --project Tests/IntegrationTests --no-build --verbosity normal
```
### Run All Tests
```bash
-dotnet test --no-build --verbosity normal
+dotnet test --project Tests/UnitTests --no-build --verbosity normal && dotnet test --project Tests/UnitTestsParallelizable --no-build --verbosity normal
```
## Common Build Issues
@@ -94,7 +94,7 @@ dotnet restore ./Examples/SelfContained/SelfContained.csproj -f
**For clean builds, always run in this order:**
```bash
-dotnet restore && dotnet build --no-restore && dotnet test --no-build
+dotnet restore && dotnet build --no-restore && dotnet test --project Tests/UnitTests --no-build && dotnet test --project Tests/UnitTestsParallelizable --no-build
```
This ensures:
diff --git a/.claude/workflows/pr-workflow.md b/.claude/workflows/pr-workflow.md
index 56731edfaa..26f5b9b593 100644
--- a/.claude/workflows/pr-workflow.md
+++ b/.claude/workflows/pr-workflow.md
@@ -27,7 +27,7 @@ Before submitting a PR, ensure:
- [ ] **Build passes** locally: `dotnet build --no-restore`
-- [ ] **Tests pass** locally: `dotnet test --no-build`
+- [ ] **Tests pass** locally: `dotnet test --project Tests/UnitTests --no-build && dotnet test --project Tests/UnitTestsParallelizable --no-build`
## PR Description Template
@@ -70,16 +70,14 @@ dotnet build --configuration Debug --no-restore
### 2. Run Tests
```bash
-dotnet test --no-build --verbosity normal
+dotnet test --project Tests/UnitTests --no-build --verbosity normal && dotnet test --project Tests/UnitTestsParallelizable --no-build --verbosity normal
```
**Expected:** All tests pass
### 3. Check Coverage
-Coverage is automatically collected on Linux runners in CI.
-
-**Verify coverage didn't decrease** by checking Codecov report on PR.
+Coverage collection is temporarily disabled in CI during the xUnit v3 / MTP migration.
### 4. Format Code
diff --git a/.github/workflows/README.md b/.github/workflows/README.md
index 54a83d910c..0fe1570677 100644
--- a/.github/workflows/README.md
+++ b/.github/workflows/README.md
@@ -2,12 +2,16 @@
The repository uses multiple GitHub Actions workflows. What runs and when:
-### 1) Build Solution (`.github/workflows/build.yml`)
+> **Note:** Tests use xUnit v3 with Microsoft Testing Platform (MTP). The test runner is
+> configured in `global.json` via `"test": { "runner": "Microsoft.Testing.Platform" }`.
+> MTP runs test projects as standalone executables, so each OS must build its own binaries.
-- **Triggers**: push and pull_request to `v2_release`, `v2_develop` (ignores `**.md`); supports `workflow_call`
+### 1) Build Validation (`.github/workflows/build-validation.yml`)
+
+- **Triggers**: push and pull_request to `v2_release`, `v2_develop` (ignores `**.md`)
- **Runner/timeout**: `ubuntu-latest`, 10 minutes
- **Steps**:
-- Checkout and setup .NET 8.x GA
+- Checkout and setup .NET 10.x GA
- `dotnet restore`
- Build Debug: `dotnet build --configuration Debug --no-restore -property:NoWarn=0618%3B0612`
- Build Release (library): `dotnet build Terminal.Gui/Terminal.Gui.csproj --configuration Release --no-incremental --force -property:NoWarn=0618%3B0612`
@@ -15,27 +19,20 @@ The repository uses multiple GitHub Actions workflows. What runs and when:
- Restore NativeAot/SelfContained examples, then restore solution again
- Build Release for `Examples/NativeAot` and `Examples/SelfContained`
- Build Release solution
-- Upload artifacts named `build-artifacts`, retention 1 day
### 2) Build & Run Unit Tests (`.github/workflows/unit-tests.yml`)
- **Triggers**: push and pull_request to `v2_release`, `v2_develop` (ignores `**.md`)
- **Matrix**: Ubuntu/Windows/macOS
-- **Timeout**: 15 minutes per job
+- **Timeout**: 15 minutes (non-parallel), 60 minutes (parallel)
- **Process**:
-1. Calls build workflow to build solution once
-2. Downloads build artifacts
-3. Runs `dotnet restore` (required for `--no-build` to work)
-4. **Performance optimizations**:
+1. Each OS checks out code, restores, and builds locally
+2. **Performance optimizations**:
- Disables Windows Defender on Windows runners (significant speedup)
- - Collects code coverage **only on Linux** (ubuntu-latest) for performance
- - Windows and macOS skip coverage collection to reduce test time
- - Increased blame-hang-timeout to 120s for Windows/macOS (60s for Linux)
-5. Runs two test jobs:
- - **Non-parallel UnitTests**: `Tests/UnitTests` with blame/diag flags; `xunit.stopOnFail=false`
- - **Parallel UnitTestsParallelizable**: `Tests/UnitTestsParallelizable` with blame/diag flags; `xunit.stopOnFail=false`
-6. Uploads test logs and diagnostic data from all runners
-7. **Uploads code coverage to Codecov only from Linux runner**
+3. Runs two test jobs:
+ - **Non-parallel UnitTests**: `Tests/UnitTests` with diagnostic output
+ - **Parallel UnitTestsParallelizable**: `Tests/UnitTestsParallelizable` with diagnostic output
+4. Uploads test logs and diagnostic data from all runners
**Test results**: All tests output to unified `TestResults/` directory at repository root
@@ -45,16 +42,11 @@ The repository uses multiple GitHub Actions workflows. What runs and when:
- **Matrix**: Ubuntu/Windows/macOS
- **Timeout**: 15 minutes
- **Process**:
-1. Calls build workflow
-2. Downloads build artifacts
-3. Runs `dotnet restore`
-4. **Performance optimizations** (same as unit tests):
+1. Each OS checks out code, restores, and builds locally
+2. **Performance optimizations**:
- Disables Windows Defender on Windows runners
- - Collects code coverage **only on Linux**
- - Increased blame-hang-timeout to 120s for Windows/macOS
-5. Runs IntegrationTests with blame/diag flags; `xunit.stopOnFail=true`
-6. Uploads logs per-OS
-7. **Uploads coverage to Codecov only from Linux runner**
+3. Runs IntegrationTests with diagnostic output
+4. Uploads logs per-OS
### 4) Create Release (`.github/workflows/release.yml`)
@@ -95,7 +87,7 @@ The repository uses multiple GitHub Actions workflows. What runs and when:
# Full CI sequence:
dotnet restore
dotnet build --configuration Debug --no-restore
-dotnet test Tests/UnitTests --no-build --verbosity normal
-dotnet test Tests/UnitTestsParallelizable --no-build --verbosity normal
+dotnet test --project Tests/UnitTests --no-build --verbosity normal
+dotnet test --project Tests/UnitTestsParallelizable --no-build --verbosity normal
dotnet build --configuration Release --no-restore
```
diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index c1ec99668c..d41181adfe 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -10,15 +10,11 @@ on:
- '**.md'
jobs:
- build:
- uses: ./.github/workflows/quick-build.yml
-
integration_tests:
name: Integration Tests
runs-on: ${{ matrix.os }}
- needs: build
strategy:
- fail-fast: false # Let all OSes finish even if one fails
+ fail-fast: false
matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ]
timeout-minutes: 15
@@ -33,57 +29,27 @@ jobs:
dotnet-version: 10.x
dotnet-quality: ga
- - name: Download build artifacts
- uses: actions/download-artifact@v4
- with:
- name: test-build-artifacts
- path: .
-
- name: Restore NuGet packages
run: dotnet restore
+ - name: Build
+ run: dotnet build --no-restore
+
- name: Disable Windows Defender (Windows only)
if: runner.os == 'Windows'
shell: powershell
run: |
Add-MpPreference -ExclusionPath "${{ github.workspace }}"
Add-MpPreference -ExclusionProcess "dotnet.exe"
- Add-MpPreference -ExclusionProcess "testhost.exe"
- Add-MpPreference -ExclusionProcess "VSTest.Console.exe"
-
- - name: Set VSTEST_DUMP_PATH
- shell: bash
- run: echo "VSTEST_DUMP_PATH=logs/IntegrationTests/${{ runner.os }}/" >> $GITHUB_ENV
- name: Run IntegrationTests
shell: bash
run: |
- if [ "${{ runner.os }}" == "Linux" ]; then
- # Run with coverage on Linux only
- dotnet test Tests/IntegrationTests \
- --no-build \
- --verbosity minimal \
- --collect:"XPlat Code Coverage" \
- --settings Tests/IntegrationTests/runsettings.coverage.xml \
- --diag:logs/IntegrationTests/${{ runner.os }}/logs.txt \
- --blame \
- --blame-crash \
- --blame-hang \
- --blame-hang-timeout 60s \
- --blame-crash-collect-always
- else
- # Run without coverage on Windows/macOS for speed
- dotnet test Tests/IntegrationTests \
- --no-build \
- --verbosity minimal \
- --settings Tests/IntegrationTests/runsettings.xml \
- --diag:logs/IntegrationTests/${{ runner.os }}/logs.txt \
- --blame \
- --blame-crash \
- --blame-hang \
- --blame-hang-timeout 60s \
- --blame-crash-collect-always
- fi
+ dotnet test \
+ --project Tests/IntegrationTests \
+ --no-build \
+ --verbosity minimal \
+ --diagnostic --diagnostic-output-directory logs/IntegrationTests/${{ runner.os }}
- name: Upload Integration Test Logs
if: always()
@@ -93,14 +59,3 @@ jobs:
path: |
logs/IntegrationTests/
TestResults/
-
- - name: Upload Integration Tests Coverage to Codecov
- if: matrix.os == 'ubuntu-latest' && always()
- uses: codecov/codecov-action@v4
- with:
- files: TestResults/**/coverage.cobertura.xml
- flags: integrationtests
- name: IntegrationTests-${{ runner.os }}
- token: ${{ secrets.CODECOV_TOKEN }}
- fail_ci_if_error: false
-
diff --git a/.github/workflows/stress-tests.yml b/.github/workflows/stress-tests.yml
index 2298f60126..ffbb23e566 100644
--- a/.github/workflows/stress-tests.yml
+++ b/.github/workflows/stress-tests.yml
@@ -7,7 +7,7 @@ on:
branches: [ v2_release, v2_develop ]
paths-ignore:
- '**.md'
-
+
jobs:
run_stress_tests:
runs-on: ${{ matrix.os }}
@@ -37,7 +37,7 @@ jobs:
run: |
end=$((SECONDS+900))
while [ $SECONDS -lt $end ]; do
- dotnet test Tests/StressTests --no-build --verbosity normal --diag:logs/${{ runner.os }}/logs.txt --blame --blame-crash --blame-hang --blame-hang-timeout 60s --blame-crash-collect-always -- xunit.stopOnFail=true
+ dotnet test --project Tests/StressTests --no-build --verbosity normal --diagnostic --diagnostic-output-directory logs/${{ runner.os }}
done
- name: Upload Test Logs
@@ -80,62 +80,31 @@ jobs:
run: |
Add-MpPreference -ExclusionPath "${{ github.workspace }}"
Add-MpPreference -ExclusionProcess "dotnet.exe"
- Add-MpPreference -ExclusionProcess "testhost.exe"
- Add-MpPreference -ExclusionProcess "VSTest.Console.exe"
-
- - name: Set VSTEST_DUMP_PATH
- shell: bash
- run: echo "VSTEST_DUMP_PATH=logs/UnitTestsParallelizable/${{ runner.os }}/" >> $GITHUB_ENV
- - name: Run UnitTestsParallelizable (3 iterations with varying parallelization)
+ - name: Run UnitTestsParallelizable (3 iterations)
shell: bash
run: |
- # Run tests 3 times with different parallelization settings to expose concurrency issues
- # Run 1: Default parallelization (2x) - standard test execution
- # Run 2: Maximum parallelization (unlimited) - stress test with high concurrency
- # Run 3: Single-threaded execution (1) - deterministic execution to expose ordering issues
+ # Run tests 3 times to expose concurrency issues
for RUN in {1..3}; do
echo "============================================"
echo "Starting test run $RUN of 3"
echo "============================================"
-
- # Use a combination of run number and timestamp to create different execution patterns
- SEED=$((1000 + $RUN + $(date +%s) % 1000))
- echo "Using randomization seed: $SEED"
-
- # Vary the xUnit parallelization based on run number to expose race conditions
- if [ $RUN -eq 1 ]; then
- XUNIT_MAX_PARALLEL_THREADS="2x"
- echo "Run $RUN: Using default parallelization (2x)"
- elif [ $RUN -eq 2 ]; then
- XUNIT_MAX_PARALLEL_THREADS="unlimited"
- echo "Run $RUN: Using maximum parallelization (unlimited)"
- else
- XUNIT_MAX_PARALLEL_THREADS="1"
- echo "Run $RUN: Using single-threaded execution"
- fi
-
- dotnet test Tests/UnitTestsParallelizable \
+
+ dotnet test \
+ --project Tests/UnitTestsParallelizable \
--no-build \
--verbosity normal \
- --settings Tests/UnitTestsParallelizable/runsettings.xml \
- --diag:logs/UnitTestsParallelizable/${{ runner.os }}/run${RUN}-logs.txt \
- --blame \
- --blame-crash \
- --blame-hang \
- --blame-hang-timeout 60s \
- --blame-crash-collect-always \
- -- xUnit.MaxParallelThreads=${XUNIT_MAX_PARALLEL_THREADS}
-
+ --diagnostic --diagnostic-output-directory logs/UnitTestsParallelizable/${{ runner.os }}/run${RUN}
+
if [ $? -ne 0 ]; then
echo "ERROR: Test run $RUN failed!"
exit 1
fi
-
+
echo "Test run $RUN completed successfully"
echo ""
done
-
+
echo "============================================"
echo "All 3 test runs completed successfully!"
echo "============================================"
@@ -148,4 +117,3 @@ jobs:
path: |
logs/UnitTestsParallelizable/
TestResults/
-
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index 96b2429ac6..181333709a 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -9,23 +9,17 @@ on:
branches: [ v2_release, v2_develop ]
paths-ignore:
- '**.md'
-
-jobs:
- # Call the quick-build workflow to build Debug configuration only
- build:
- uses: ./.github/workflows/quick-build.yml
+jobs:
non_parallel_unittests:
- name: Non-Parallel Unit Tests
+ name: Non-Parallel Unit Tests
runs-on: ${{ matrix.os }}
- needs: build
strategy:
- # Turn off fail-fast to let all runners run even if there are errors
fail-fast: false
matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ]
- timeout-minutes: 15 # Increased from 10 for Windows
+ timeout-minutes: 15
steps:
- name: Checkout code
@@ -37,16 +31,12 @@ jobs:
dotnet-version: 10.x
dotnet-quality: 'ga'
- - name: Download build artifacts
- uses: actions/download-artifact@v4
- with:
- name: test-build-artifacts
- path: .
-
- # KEEP THIS - It's needed for --no-build to work
- name: Restore NuGet packages
run: dotnet restore
+ - name: Build
+ run: dotnet build --no-restore
+
# Optimize Windows performance
- name: Disable Windows Defender (Windows only)
if: runner.os == 'Windows'
@@ -54,42 +44,15 @@ jobs:
run: |
Add-MpPreference -ExclusionPath "${{ github.workspace }}"
Add-MpPreference -ExclusionProcess "dotnet.exe"
- Add-MpPreference -ExclusionProcess "testhost.exe"
- Add-MpPreference -ExclusionProcess "VSTest.Console.exe"
-
- - name: Set VSTEST_DUMP_PATH
- shell: bash
- run: echo "VSTEST_DUMP_PATH=logs/UnitTests/${{ runner.os }}/" >> $GITHUB_ENV
- name: Run UnitTests
shell: bash
run: |
- if [ "${{ runner.os }}" == "Linux" ]; then
- # Run with coverage on Linux only
- dotnet test Tests/UnitTests \
- --no-build \
- --verbosity normal \
- --collect:"XPlat Code Coverage" \
- --settings Tests/UnitTests/runsettings.xml \
- --diag:logs/UnitTests/${{ runner.os }}/logs.txt \
- --blame \
- --blame-crash \
- --blame-hang \
- --blame-hang-timeout 60s \
- --blame-crash-collect-always
- else
- # Run without coverage on Windows/macOS for speed
- dotnet test Tests/UnitTests \
- --no-build \
- --verbosity normal \
- --settings Tests/UnitTests/runsettings.xml \
- --diag:logs/UnitTests/${{ runner.os }}/logs.txt \
- --blame \
- --blame-crash \
- --blame-hang \
- --blame-hang-timeout 120s \
- --blame-crash-collect-always
- fi
+ dotnet test \
+ --project Tests/UnitTests \
+ --no-build \
+ --verbosity normal \
+ --diagnostic --diagnostic-output-directory logs/UnitTests/${{ runner.os }}
- name: Upload Test Logs
if: always()
@@ -103,23 +66,11 @@ jobs:
**/*.dmp
if-no-files-found: ignore
retention-days: 7
-
- - name: Upload Non-Parallel UnitTests Coverage to Codecov
- if: matrix.os == 'ubuntu-latest' && always()
- uses: codecov/codecov-action@v4
- with:
- files: TestResults/**/coverage.cobertura.xml
- flags: unittests-nonparallel
- name: UnitTests-${{ runner.os }}
- token: ${{ secrets.CODECOV_TOKEN }}
- fail_ci_if_error: false
parallel_unittests:
- name: Parallel Unit Tests
+ name: Parallel Unit Tests
runs-on: ${{ matrix.os }}
- needs: build
strategy:
- # Turn off fail-fast to let all runners run even if there are errors
fail-fast: false
matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ]
@@ -136,87 +87,28 @@ jobs:
dotnet-version: 10.x
dotnet-quality: 'ga'
- - name: Download build artifacts
- uses: actions/download-artifact@v4
- with:
- name: test-build-artifacts
- path: .
-
- name: Restore NuGet packages
run: dotnet restore
+ - name: Build
+ run: dotnet build --no-restore
+
- name: Disable Windows Defender (Windows only)
if: runner.os == 'Windows'
shell: powershell
run: |
Add-MpPreference -ExclusionPath "${{ github.workspace }}"
Add-MpPreference -ExclusionProcess "dotnet.exe"
- Add-MpPreference -ExclusionProcess "testhost.exe"
- Add-MpPreference -ExclusionProcess "VSTest.Console.exe"
-
- - name: Set VSTEST_DUMP_PATH
- shell: bash
- run: echo "VSTEST_DUMP_PATH=logs/UnitTestsParallelizable/${{ runner.os }}/" >> $GITHUB_ENV
- name: Run UnitTestsParallelizable
shell: bash
run: |
- # Detect CPU count and calculate optimal thread count
- if [ "${{ runner.os }}" == "Linux" ]; then
- CPU_COUNT=$(nproc)
- elif [ "${{ runner.os }}" == "macOS" ]; then
- CPU_COUNT=$(sysctl -n hw.ncpu)
- else # Windows
- CPU_COUNT=$NUMBER_OF_PROCESSORS
- fi
-
- # Use 2x CPU count for I/O-bound tests, capped at reasonable max
- # macOS uses lower cap (4) to prevent thread pool exhaustion
- MAX_THREADS=$((CPU_COUNT * 2))
- if [ "${{ runner.os }}" == "macOS" ]; then
- if [ $MAX_THREADS -gt 4 ]; then
- MAX_THREADS=2
- fi
- HANG_TIMEOUT="240s"
- else
- if [ $MAX_THREADS -gt 16 ]; then
- MAX_THREADS=16
- fi
- HANG_TIMEOUT="60s"
- fi
-
- echo "Detected $CPU_COUNT CPUs, using $MAX_THREADS parallel threads"
-
- # Run tests with dynamic thread count
- if [ "${{ runner.os }}" == "Linux" ]; then
- dotnet test Tests/UnitTestsParallelizable \
- --no-build \
- --verbosity normal \
- --collect:"XPlat Code Coverage" \
- --settings Tests/UnitTests/runsettings.coverage.xml \
- --diag:logs/UnitTestsParallelizable/${{ runner.os }}/logs.txt \
- --blame \
- --blame-crash \
- --blame-hang \
- --blame-hang-timeout $HANG_TIMEOUT \
- --blame-crash-collect-always \
- -- xUnit.MaxParallelThreads=$MAX_THREADS \
- -- xUnit.StopOnFail=true
- else
- dotnet test Tests/UnitTestsParallelizable \
- --no-build \
- --verbosity normal \
- --settings Tests/UnitTestsParallelizable/runsettings.xml \
- --diag:logs/UnitTestsParallelizable/${{ runner.os }}/logs.txt \
- --blame \
- --blame-crash \
- --blame-hang \
- --blame-hang-timeout $HANG_TIMEOUT \
- --blame-crash-collect-always \
- -- xUnit.MaxParallelThreads=$MAX_THREADS \
- -- xUnit.StopOnFail=true
- fi
-
+ dotnet test \
+ --project Tests/UnitTestsParallelizable \
+ --no-build \
+ --verbosity normal \
+ --diagnostic --diagnostic-output-directory logs/UnitTestsParallelizable/${{ runner.os }}
+
echo "============================================"
echo "Parallel unit tests completed successfully!"
echo "============================================"
@@ -233,13 +125,3 @@ jobs:
**/*.dmp
if-no-files-found: ignore
retention-days: 7
-
- - name: Upload Parallelizable UnitTests Coverage to Codecov
- if: matrix.os == 'ubuntu-latest' && always()
- uses: codecov/codecov-action@v4
- with:
- files: TestResults/**/coverage.cobertura.xml
- flags: unittests-parallel
- name: UnitTestsParallelizable-${{ runner.os }}
- token: ${{ secrets.CODECOV_TOKEN }}
- fail_ci_if_error: false
diff --git a/AGENTS.md b/AGENTS.md
index 0275642d2b..37238ac059 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -56,7 +56,7 @@ dotnet run
**Terminal.Gui** - Cross-platform console UI toolkit for .NET (C# 12, net8.0)
**Build:** `dotnet restore && dotnet build --no-restore`
-**Test:** `dotnet test --no-build`
+**Test:** `dotnet test --project Tests/UnitTests --no-build && dotnet test --project Tests/UnitTestsParallelizable --no-build`
**Details:** [Build & Test Workflow](.claude/workflows/build-test-workflow.md)
## Quick Rules
diff --git a/CLAUDE.md b/CLAUDE.md
index 5973460009..ac89ea232b 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -81,8 +81,8 @@ When in planning mode:
```bash
dotnet restore
dotnet build --no-restore
-dotnet test Tests/UnitTestsParallelizable --no-build
-dotnet test Tests/UnitTests --no-build
+dotnet test --project Tests/UnitTestsParallelizable --no-build
+dotnet test --project Tests/UnitTests --no-build
```
## Key Concepts
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2b556c5a00..0b1abf6c57 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -76,22 +76,22 @@ Welcome! This guide provides everything you need to know to contribute effective
1. **Non-parallel tests** (depend on static state, ~10 min timeout):
```bash
- dotnet test Tests/UnitTests --no-build --verbosity normal
+ dotnet test --project Tests/UnitTests --no-build --verbosity normal
```
- Uses `Application.Init` and static state
- Cannot run in parallel
- - Includes `--blame` flags for crash diagnostics
+ - Includes `--diagnostic` flag for logging
2. **Parallel tests** (can run concurrently, ~10 min timeout):
```bash
- dotnet test Tests/UnitTestsParallelizable --no-build --verbosity normal
+ dotnet test --project Tests/UnitTestsParallelizable --no-build --verbosity normal
```
- No dependencies on static state
- **Preferred for new tests**
3. **Integration tests**:
```bash
- dotnet test Tests/IntegrationTests --no-build --verbosity normal
+ dotnet test --project Tests/IntegrationTests --no-build --verbosity normal
```
### Common Build Issues
@@ -176,11 +176,8 @@ Welcome! This guide provides everything you need to know to contribute effective
- **Never decrease code coverage** - PRs must maintain or increase coverage
- Target: 70%+ coverage for new code
- **Coverage collection**:
-- Centralized in `TestResults/` directory at repository root
-- Collected only on Linux (ubuntu-latest) runners in CI for performance
-- Windows and macOS runners skip coverage collection to reduce execution time
-- Coverage reports uploaded to Codecov automatically from Linux runner
-- CI monitors coverage on each PR
+- Temporarily disabled in CI during xUnit v3 / MTP migration
+- Will be re-enabled once an MTP-compatible coverage solution is integrated
### Test Patterns
@@ -195,7 +192,7 @@ Welcome! This guide provides everything you need to know to contribute effective
### Test Configuration
- `xunit.runner.json` - xUnit configuration
-- `coverlet.runsettings` - Coverage settings (OpenCover format)
+- `coverlet.runsettings` - Coverage settings (currently unused, pending MTP integration)
## API Documentation Requirements
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 34805646a7..b699b270a7 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -6,43 +6,43 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/ReactiveExample/LoginView.cs b/Examples/ReactiveExample/LoginView.cs
index 0eb5e7a345..4090d2694d 100644
--- a/Examples/ReactiveExample/LoginView.cs
+++ b/Examples/ReactiveExample/LoginView.cs
@@ -1,4 +1,5 @@
using System.Reactive.Disposables;
+using System.Reactive.Disposables.Fluent;
using System.Reactive.Linq;
using ReactiveMarbles.ObservableEvents;
using ReactiveUI;
@@ -138,7 +139,7 @@ public LoginView (LoginViewModel viewModel)
ViewModel
.WhenAnyObservable (x => x.Login.IsExecuting)
.Select (executing => executing ? ProgressMessage : IdleMessage)
- .ObserveOn (RxApp.MainThreadScheduler)
+ .ObserveOn (Program._rxApp.MainThreadScheduler!)
.BindTo (progress, x => x.Text)
.DisposeWith (_disposable);
});
diff --git a/Examples/ReactiveExample/Program.cs b/Examples/ReactiveExample/Program.cs
index e70611afc5..fd7572b69d 100644
--- a/Examples/ReactiveExample/Program.cs
+++ b/Examples/ReactiveExample/Program.cs
@@ -1,5 +1,5 @@
using System.Reactive.Concurrency;
-using ReactiveUI;
+using ReactiveUI.Builder;
using Terminal.Gui.App;
using Terminal.Gui.Configuration;
@@ -7,14 +7,17 @@ namespace ReactiveExample;
public static class Program
{
- private static void Main (string [] args)
+ internal static ReactiveUIBuilder _rxApp;
+
+ private static void Main (string [] _)
{
ConfigurationManager.Enable (ConfigLocations.All);
using IApplication app = Application.Create ();
app.Init ();
- RxApp.MainThreadScheduler = new TerminalScheduler (app);
- RxApp.TaskpoolScheduler = TaskPoolScheduler.Default;
- var loginView = new LoginView (new ());
+ _rxApp = RxAppBuilder.CreateReactiveUIBuilder ();
+ _rxApp.WithMainThreadScheduler (new TerminalScheduler (app));
+ _rxApp.WithTaskPoolScheduler (TaskPoolScheduler.Default);
+ var loginView = new LoginView (new LoginViewModel ());
app.Run (loginView);
loginView.Dispose ();
}
diff --git a/Examples/ScenarioRunner/Program.cs b/Examples/ScenarioRunner/Program.cs
index 4925d55128..b2d353c46d 100644
--- a/Examples/ScenarioRunner/Program.cs
+++ b/Examples/ScenarioRunner/Program.cs
@@ -1,8 +1,6 @@
#nullable enable
using System.Collections.ObjectModel;
using System.CommandLine;
-using System.CommandLine.Builder;
-using System.CommandLine.Parsing;
using System.Reflection;
using System.Text;
using Microsoft.Extensions.Logging;
@@ -34,168 +32,178 @@ public static int Main (string [] args)
// Get allowed driver names
string? [] allowedDrivers = DriverRegistry.GetDriverNames ().ToArray ();
- Option driverOption = new Option ("--driver", "The IDriver to use.").FromAmong (allowedDrivers!);
- driverOption.SetDefaultValue (string.Empty);
- driverOption.AddAlias ("-d");
+ Option driverOption = new Option ("--driver") { Description = "The IDriver to use.", DefaultValueFactory = _ => string.Empty };
+ driverOption.AcceptOnlyFromAmong (allowedDrivers!);
+ driverOption.Aliases.Add ("-d");
- Option disableConfigManagement = new ("--disable-cm", "Indicates Configuration Management should not be enabled.");
- disableConfigManagement.AddAlias ("-dcm");
+ Option disableConfigManagement = new ("--disable-cm") { Description = "Indicates Configuration Management should not be enabled." };
+ disableConfigManagement.Aliases.Add ("-dcm");
- Option force16Colors = new ("--force-16-colors", "Forces the driver to use 16-color mode instead of TrueColor.");
- force16Colors.AddAlias ("-16");
+ Option force16Colors = new ("--force-16-colors") { Description = "Forces the driver to use 16-color mode instead of TrueColor." };
+ force16Colors.Aliases.Add ("-16");
- Option benchmarkTimeout = new ("--timeout",
- () => Scenario.BenchmarkTimeout,
- $"The maximum time in milliseconds to run a benchmark. Default is {Scenario.BenchmarkTimeout}ms.");
- benchmarkTimeout.AddAlias ("-t");
+ Option benchmarkTimeout = new ("--timeout")
+ {
+ Description = $"The maximum time in milliseconds to run a benchmark. Default is {Scenario.BenchmarkTimeout}ms.",
+ DefaultValueFactory = _ => Scenario.BenchmarkTimeout
+ };
+ benchmarkTimeout.Aliases.Add ("-t");
- Option resultsFile = new ("--file", "The file to save benchmark results to.");
- resultsFile.AddAlias ("-f");
+ Option resultsFile = new ("--file") { Description = "The file to save benchmark results to." };
+ resultsFile.Aliases.Add ("-f");
LogFilePath = $"{LOGFILE_LOCATION}/{Assembly.GetExecutingAssembly ().GetName ().Name}";
- Option debugLogLevel = new Option ("--debug-log-level", "The level to use for logging.").FromAmong (Enum.GetNames ());
- debugLogLevel.SetDefaultValue ("Warning");
- debugLogLevel.AddAlias ("-dl");
+ Option debugLogLevel = new Option ("--debug-log-level")
+ {
+ Description = "The level to use for logging.", DefaultValueFactory = _ => "Warning"
+ };
+ debugLogLevel.AcceptOnlyFromAmong (Enum.GetNames ());
+ debugLogLevel.Aliases.Add ("-dl");
// List command
Command listCommand = new ("list", "List all available scenarios");
- listCommand.SetHandler (() =>
- {
- ObservableCollection scenarios = Scenario.GetScenarios ();
+ listCommand.SetAction (_ =>
+ {
+ ObservableCollection scenarios = Scenario.GetScenarios ();
- Console.WriteLine (@$"Available scenarios ({scenarios.Count})");
- Console.WriteLine ();
+ Console.WriteLine (@$"Available scenarios ({scenarios.Count})");
+ Console.WriteLine ();
- foreach (Scenario s in scenarios)
- {
- Console.WriteLine (@$" {s.GetName (),-30} {s.GetDescription ()}");
- }
- });
+ foreach (Scenario s in scenarios)
+ {
+ Console.WriteLine (@$" {s.GetName (),-30} {s.GetDescription ()}");
+ }
+ });
// Run command
- Argument scenarioArgument = new ("scenario", "The name of the Scenario to run.");
+ Argument scenarioArgument = new ("scenario") { Description = "The name of the Scenario to run." };
Command runCommand = new ("run", "Run a specific scenario") { scenarioArgument };
- runCommand.AddOption (driverOption);
- runCommand.AddOption (disableConfigManagement);
- runCommand.AddOption (force16Colors);
- runCommand.AddOption (debugLogLevel);
+ runCommand.Options.Add (driverOption);
+ runCommand.Options.Add (disableConfigManagement);
+ runCommand.Options.Add (force16Colors);
+ runCommand.Options.Add (debugLogLevel);
- runCommand.SetHandler ((scenarioName, driver, disableCm, force16, logLevel) =>
- {
- SetupLogging (logLevel);
+ runCommand.SetAction (parseResult =>
+ {
+ // Extract the values using parseResult.
+ string scenarioName = parseResult.GetRequiredValue (scenarioArgument);
+ string driver = parseResult.GetRequiredValue (driverOption);
+ bool disableCm = parseResult.GetRequiredValue (disableConfigManagement);
+ bool force16 = parseResult.GetRequiredValue (force16Colors);
+ string logLevel = parseResult.GetRequiredValue (debugLogLevel);
- Runner runner = new ();
+ // Executing the original logic
+ SetupLogging (logLevel);
- if (!disableCm)
- {
- runner.SetRuntimeConfig(driver, force16 ? true : null);
- ConfigurationManager.Enable (ConfigLocations.All);
- }
+ Runner runner = new ();
- Scenario? scenario = FindScenario (scenarioName);
+ if (!disableCm)
+ {
+ runner.SetRuntimeConfig (driver, force16 ? true : null);
+ ConfigurationManager.Enable (ConfigLocations.All);
+ }
- if (scenario is null)
- {
- Console.Error.WriteLine ($"Scenario '{scenarioName}' not found.");
+ Scenario? scenario = FindScenario (scenarioName);
- return;
- }
+ if (scenario is null)
+ {
+ Console.Error.WriteLine ($"Scenario '{scenarioName}' not found.");
- // Pass force16 only if explicitly set (default false means not set)
- runner.RunScenario (scenarioName, false);
- },
- scenarioArgument,
- driverOption,
- disableConfigManagement,
- force16Colors,
- debugLogLevel);
+ return; // SetAction returns void, so the empty return value works.
+ }
+
+ // Pass force16 only if explicitly set (default false means not set)
+ runner.RunScenario (scenarioName, false);
+ });
// Benchmark command
- Argument benchmarkScenarioArgument =
- new ("scenario", () => null, "The name of the Scenario to benchmark. If not specified, all scenarios are benchmarked.");
+ Argument benchmarkScenarioArgument = new ("scenario")
+ {
+ Description = "The name of the Scenario to benchmark. If not specified, all scenarios are benchmarked.", DefaultValueFactory = _ => null
+ };
Command benchmarkCommand = new ("benchmark", "Benchmark scenarios") { benchmarkScenarioArgument };
- benchmarkCommand.AddOption (driverOption);
- benchmarkCommand.AddOption (disableConfigManagement);
- benchmarkCommand.AddOption (force16Colors);
- benchmarkCommand.AddOption (benchmarkTimeout);
- benchmarkCommand.AddOption (resultsFile);
- benchmarkCommand.AddOption (debugLogLevel);
-
- benchmarkCommand.SetHandler ((scenarioName, driver, disableCm, force16, timeout, file, logLevel) =>
- {
- SetupLogging (logLevel);
- Scenario.BenchmarkTimeout = timeout;
-
- Runner runner = new ();
-
- if (!disableCm)
- {
- // Pass force16 only if explicitly set
- runner.SetRuntimeConfig (driver, force16 ? true : null);
- ConfigurationManager.Enable (ConfigLocations.All);
- }
-
- List results;
-
- if (string.IsNullOrEmpty (scenarioName))
- {
- // Benchmark all scenarios
- ObservableCollection scenarios = Scenario.GetScenarios ();
- results = runner.BenchmarkAllScenarios (scenarios);
- }
- else
- {
- // Benchmark single scenario
- Scenario? scenario = FindScenario (scenarioName);
-
- if (scenario is null)
- {
- Console.Error.WriteLine ($"Scenario '{scenarioName}' not found.");
-
- return;
- }
-
- BenchmarkResults? result = runner.RunScenario (scenarioName, true);
- results = result is { } ? [result] : [];
- }
-
- if (results.Count == 0)
- {
- Console.WriteLine (@"No benchmark results collected.");
-
- return;
- }
-
- if (!string.IsNullOrEmpty (file))
- {
- Runner.SaveResultsToFile (results, file);
- Console.WriteLine (@$"Results saved to {file}");
- }
- else
- {
- // Display in UI
- Runner.DisplayResultsUI (results);
- }
- },
- benchmarkScenarioArgument,
- driverOption,
- disableConfigManagement,
- force16Colors,
- benchmarkTimeout,
- resultsFile,
- debugLogLevel);
+ benchmarkCommand.Options.Add (driverOption);
+ benchmarkCommand.Options.Add (disableConfigManagement);
+ benchmarkCommand.Options.Add (force16Colors);
+ benchmarkCommand.Options.Add (benchmarkTimeout);
+ benchmarkCommand.Options.Add (resultsFile);
+ benchmarkCommand.Options.Add (debugLogLevel);
- RootCommand rootCommand = new ("Terminal.Gui Scenario Runner - Run and benchmark Terminal.Gui scenarios") { listCommand, runCommand, benchmarkCommand };
+ benchmarkCommand.SetAction (parseResult =>
+ {
+ // Extract the values using parseResult.
+ string scenarioName = parseResult.GetRequiredValue (scenarioArgument);
+ string driver = parseResult.GetRequiredValue (driverOption);
+ bool disableCm = parseResult.GetRequiredValue (disableConfigManagement);
+ bool force16 = parseResult.GetRequiredValue (force16Colors);
+ uint timeout = parseResult.GetRequiredValue (benchmarkTimeout);
+ string file = parseResult.GetRequiredValue (resultsFile);
+ string logLevel = parseResult.GetRequiredValue (debugLogLevel);
+
+ SetupLogging (logLevel);
+ Scenario.BenchmarkTimeout = timeout;
+
+ Runner runner = new ();
+
+ if (!disableCm)
+ {
+ // Pass force16 only if explicitly set
+ runner.SetRuntimeConfig (driver, force16 ? true : null);
+ ConfigurationManager.Enable (ConfigLocations.All);
+ }
+
+ List results;
+
+ if (string.IsNullOrEmpty (scenarioName))
+ {
+ // Benchmark all scenarios
+ ObservableCollection scenarios = Scenario.GetScenarios ();
+ results = runner.BenchmarkAllScenarios (scenarios);
+ }
+ else
+ {
+ // Benchmark single scenario
+ Scenario? scenario = FindScenario (scenarioName);
+
+ if (scenario is null)
+ {
+ Console.Error.WriteLine ($"Scenario '{scenarioName}' not found.");
+
+ return;
+ }
+
+ BenchmarkResults? result = runner.RunScenario (scenarioName, true);
+ results = result is { } ? [result] : [];
+ }
+
+ if (results.Count == 0)
+ {
+ Console.WriteLine (@"No benchmark results collected.");
+
+ return;
+ }
+
+ if (!string.IsNullOrEmpty (file))
+ {
+ Runner.SaveResultsToFile (results, file);
+ Console.WriteLine (@$"Results saved to {file}");
+ }
+ else
+ {
+ // Display in UI
+ Runner.DisplayResultsUI (results);
+ }
+ });
- Parser parser = new CommandLineBuilder (rootCommand).UseDefaults ().Build ();
+ RootCommand rootCommand = new ("Terminal.Gui Scenario Runner - Run and benchmark Terminal.Gui scenarios") { listCommand, runCommand, benchmarkCommand };
- return parser.Invoke (args);
+ return rootCommand.Parse (args).Invoke ();
}
private static Scenario? FindScenario (string name)
diff --git a/Examples/UICatalog/Scenarios/Editor.cs b/Examples/UICatalog/Scenarios/Editor.cs
index 602184cd2a..2569539709 100644
--- a/Examples/UICatalog/Scenarios/Editor.cs
+++ b/Examples/UICatalog/Scenarios/Editor.cs
@@ -107,7 +107,7 @@ public override void Main ()
Title = "ForceMinimumPosTo_Zero", Value = _forceMinimumPosToZero ? CheckState.Checked : CheckState.UnChecked
};
- _miForceMinimumPosToZeroCheckBox.ValueChanging += (s, e) =>
+ _miForceMinimumPosToZeroCheckBox.ValueChanged += (s, e) =>
{
_forceMinimumPosToZero = e.NewValue == CheckState.Checked;
@@ -143,8 +143,7 @@ public override void Main ()
{
if (!e.Value)
{
- // BUGBUG: This should restore the original culture info
- Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
+ Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
}
};
@@ -304,7 +303,7 @@ void CreateCultureMenuItem (string title, string cultureName, bool isChecked)
allCheckBoxes.Add (checkBox);
- checkBox.ValueChanging += (s, e) =>
+ checkBox.ValueChanged += (s, e) =>
{
if (e.NewValue == CheckState.Checked)
{
@@ -744,14 +743,14 @@ private View CreateFindTab ()
{
X = 0, Y = Pos.Top (txtToFind) + 2, Value = _matchCase ? CheckState.Checked : CheckState.UnChecked, Text = "Match c_ase"
};
- ckbMatchCase.ValueChanging += (s, e) => { _matchCase = e.NewValue == CheckState.Checked; };
+ ckbMatchCase.ValueChanged += (s, e) => { _matchCase = e.NewValue == CheckState.Checked; };
d.Add (ckbMatchCase);
CheckBox ckbMatchWholeWord = new ()
{
X = 0, Y = Pos.Top (ckbMatchCase) + 1, Value = _matchWholeWord ? CheckState.Checked : CheckState.UnChecked, Text = "Match _whole word"
};
- ckbMatchWholeWord.ValueChanging += (s, e) => { _matchWholeWord = e.NewValue == CheckState.Checked; };
+ ckbMatchWholeWord.ValueChanged += (s, e) => { _matchWholeWord = e.NewValue == CheckState.Checked; };
d.Add (ckbMatchWholeWord);
return d;
@@ -824,14 +823,14 @@ private View CreateReplaceTab ()
{
X = 0, Y = Pos.Top (txtToFind) + 2, Value = _matchCase ? CheckState.Checked : CheckState.UnChecked, Text = "Match c_ase"
};
- ckbMatchCase.ValueChanging += (s, e) => { _matchCase = e.NewValue == CheckState.Checked; };
+ ckbMatchCase.ValueChanged += (s, e) => { _matchCase = e.NewValue == CheckState.Checked; };
d.Add (ckbMatchCase);
CheckBox ckbMatchWholeWord = new ()
{
X = 0, Y = Pos.Top (ckbMatchCase) + 1, Value = _matchWholeWord ? CheckState.Checked : CheckState.UnChecked, Text = "Match _whole word"
};
- ckbMatchWholeWord.ValueChanging += (s, e) => { _matchWholeWord = e.NewValue == CheckState.Checked; };
+ ckbMatchWholeWord.ValueChanged += (s, e) => { _matchWholeWord = e.NewValue == CheckState.Checked; };
d.Add (ckbMatchWholeWord);
return d;
diff --git a/Examples/UICatalog/UICatalog.cs b/Examples/UICatalog/UICatalog.cs
index c6f32830eb..b6a5e2bbe6 100644
--- a/Examples/UICatalog/UICatalog.cs
+++ b/Examples/UICatalog/UICatalog.cs
@@ -10,9 +10,8 @@
global using Terminal.Gui.Text;
global using Terminal.Gui.FileServices;
global using Terminal.Gui.Resources;
-global using Terminal.Gui.Tracing;
using System.CommandLine;
-using System.CommandLine.Builder;
+using System.CommandLine.Help;
using System.CommandLine.Parsing;
using System.Diagnostics;
using System.Globalization;
@@ -81,60 +80,77 @@ private static int Main (string [] args)
// Get allowed driver names
string? [] allowedDrivers = DriverRegistry.GetDriverNames ().ToArray ();
- Option driverOption = new Option ("--driver", "The IDriver to use.").FromAmong (allowedDrivers!);
- driverOption.SetDefaultValue (string.Empty);
- driverOption.AddAlias ("-d");
- driverOption.AddAlias ("--d");
+ Option driverOption = new ("--driver")
+ {
+ Description = "The IDriver to use."
+ };
+ driverOption.AcceptOnlyFromAmong (allowedDrivers!);
+ driverOption.DefaultValueFactory = _ => string.Empty;
+ driverOption.Aliases.Add ("-d");
+ driverOption.Aliases.Add ("--d");
// Add validator separately (not chained)
- driverOption.AddValidator (result =>
+ driverOption.Validators.Add (result =>
{
var value = result.GetValueOrDefault ();
if (result.Tokens.Count > 0 && !allowedDrivers.Contains (value))
{
- result.ErrorMessage = $"Invalid driver name '{value}'. Allowed values: {string.Join (", ", allowedDrivers)}";
+ result.AddError ($"Invalid driver name '{value}'. Allowed values: {string.Join (", ", allowedDrivers)}");
}
});
// Configuration Management
- Option disableConfigManagement = new ("--disable-cm",
- "Indicates Configuration Management should not be enabled. Only `ConfigLocations.HardCoded` settings will be loaded.");
- disableConfigManagement.AddAlias ("-dcm");
- disableConfigManagement.AddAlias ("--dcm");
+ Option disableConfigManagement = new ("--disable-cm")
+ {
+ Description = "Indicates Configuration Management should not be enabled. Only `ConfigLocations.HardCoded` settings will be loaded."
+ };
+ disableConfigManagement.Aliases.Add ("-dcm");
+ disableConfigManagement.Aliases.Add ("--dcm");
- Option benchmarkFlag = new ("--benchmark", "Enables benchmarking. If a Scenario is specified, just that Scenario will be benchmarked.");
- benchmarkFlag.AddAlias ("-b");
- benchmarkFlag.AddAlias ("--b");
+ Option benchmarkFlag = new ("--benchmark")
+ {
+ Description = "Enables benchmarking. If a Scenario is specified, just that Scenario will be benchmarked."
+ };
+ benchmarkFlag.Aliases.Add ("-b");
+ benchmarkFlag.Aliases.Add ("--b");
- Option force16ColorsOption = new ("--force-16-colors", "Forces the driver to use 16-color mode instead of TrueColor.");
- force16ColorsOption.AddAlias ("-16");
+ Option force16ColorsOption = new ("--force-16-colors") { Description = "Forces the driver to use 16-color mode instead of TrueColor." };
+ force16ColorsOption.Aliases.Add ("-16");
- Option benchmarkTimeout = new ("--timeout",
- () => Scenario.BenchmarkTimeout,
- $"The maximum time in milliseconds to run a benchmark for. Default is {Scenario.BenchmarkTimeout}ms.");
- benchmarkTimeout.AddAlias ("-t");
- benchmarkTimeout.AddAlias ("--t");
+ Option benchmarkTimeout = new ("--timeout")
+ {
+ Description = $"The maximum time in milliseconds to run a benchmark for. Default is {Scenario.BenchmarkTimeout}ms.",
+ DefaultValueFactory = _ => Scenario.BenchmarkTimeout
+ };
+ benchmarkTimeout.Aliases.Add ("-t");
+ benchmarkTimeout.Aliases.Add ("--t");
- Option resultsFile = new ("--file", "The file to save benchmark results to. If not specified, the results will be displayed in a TableView.");
- resultsFile.AddAlias ("-f");
- resultsFile.AddAlias ("--f");
+ Option resultsFile = new ("--file")
+ {
+ Description = "The file to save benchmark results to. If not specified, the results will be displayed in a TableView.",
+ DefaultValueFactory = _ => string.Empty
+ };
+ resultsFile.Aliases.Add ("-f");
+ resultsFile.Aliases.Add ("--f");
// what's the app name?
LogFilePath = $"{LOGFILE_LOCATION}/{Assembly.GetExecutingAssembly ().GetName ().Name}.log";
- Option debugLogLevel =
- new Option ("--debug-log-level", $"The level to use for logging (debug console and {LogFilePath})").FromAmong (Enum.GetNames ());
- debugLogLevel.SetDefaultValue ("Warning");
- debugLogLevel.AddAlias ("-dl");
- debugLogLevel.AddAlias ("--dl");
+ Option debugLogLevel = new ("--debug-log-level")
+ {
+ Description = $"The level to use for logging (debug console and {LogFilePath})"
+ };
+ debugLogLevel.AcceptOnlyFromAmong (Enum.GetNames ());
+ debugLogLevel.DefaultValueFactory = _ => "Warning";
+ debugLogLevel.Aliases.Add ("-dl");
+ debugLogLevel.Aliases.Add ("--dl");
- Argument scenarioArgument =
- new Argument ("scenario",
- description: "The name of the Scenario to run. If not provided, the UI Catalog UI will be shown.",
- getDefaultValue: () => "none").FromAmong (UICatalogRunnable.CachedScenarios.Select (s => s.GetName ())
- .Append ("none")
- .ToArray ());
+ Argument scenarioArgument = new ("scenario")
+ {
+ Description = "The name of the Scenario to run. If not provided, the UI Catalog UI will be shown.", DefaultValueFactory = _ => "none"
+ };
+ scenarioArgument.AcceptOnlyFromAmong (UICatalogRunnable.CachedScenarios.Select (s => s.GetName ()).Append ("none").ToArray ());
var rootCommand = new RootCommand ("A comprehensive sample library and test app for Terminal.Gui")
{
@@ -148,19 +164,19 @@ private static int Main (string [] args)
force16ColorsOption
};
- rootCommand.SetHandler (context =>
+ rootCommand.SetAction (context =>
{
- bool force16 = context.ParseResult.GetValueForOption (force16ColorsOption);
+ bool force16 = context.GetRequiredValue (force16ColorsOption);
UICatalogCommandLineOptions options = new ()
{
- Scenario = context.ParseResult.GetValueForArgument (scenarioArgument),
- Driver = context.ParseResult.GetValueForOption (driverOption) ?? string.Empty,
- DontEnableConfigurationManagement = context.ParseResult.GetValueForOption (disableConfigManagement),
- Benchmark = context.ParseResult.GetValueForOption (benchmarkFlag),
- BenchmarkTimeout = context.ParseResult.GetValueForOption (benchmarkTimeout),
- ResultsFile = context.ParseResult.GetValueForOption (resultsFile) ?? string.Empty,
- DebugLogLevel = context.ParseResult.GetValueForOption (debugLogLevel) ?? "Warning",
+ Scenario = context.GetRequiredValue (scenarioArgument),
+ Driver = context.GetRequiredValue (driverOption) ?? string.Empty,
+ DontEnableConfigurationManagement = context.GetRequiredValue (disableConfigManagement),
+ Benchmark = context.GetRequiredValue (benchmarkFlag),
+ BenchmarkTimeout = context.GetRequiredValue (benchmarkTimeout),
+ ResultsFile = context.GetRequiredValue (resultsFile) ?? string.Empty,
+ DebugLogLevel = context.GetRequiredValue (debugLogLevel) ?? "Warning",
// Only set Force16Colors if explicitly specified on command line
Force16Colors = force16 ? true : null
@@ -172,17 +188,21 @@ private static int Main (string [] args)
var helpShown = false;
- Parser parser = new CommandLineBuilder (rootCommand).UseHelp (_ => helpShown = true).Build ();
+ ParseResult parseResult = rootCommand.Parse (args);
- parser.Invoke (args);
+ // Check if the analysis results indicate that help should be displayed
+ if (parseResult.Errors.Count == 0 && parseResult.Action is HelpAction)
+ {
+ helpShown = true;
+ }
+
+ parseResult.Invoke ();
if (helpShown)
{
return 0;
}
- ParseResult parseResult = parser.Parse (args);
-
if (parseResult.Errors.Count > 0)
{
foreach (ParseError error in parseResult.Errors)
diff --git a/Terminal.Gui/App/Clipboard/ClipboardProcessRunner.cs b/Terminal.Gui/App/Clipboard/ClipboardProcessRunner.cs
index 70c471d744..07f431dfd1 100644
--- a/Terminal.Gui/App/Clipboard/ClipboardProcessRunner.cs
+++ b/Terminal.Gui/App/Clipboard/ClipboardProcessRunner.cs
@@ -31,6 +31,11 @@ public static (int exitCode, string result) Process (
{
var output = string.Empty;
+ if (Console.IsInputRedirected || Console.IsOutputRedirected)
+ {
+ return (-1, output);
+ }
+
using var process = new Process ();
process.StartInfo = new()
diff --git a/Terminal.Gui/Drivers/Input/IInput.cs b/Terminal.Gui/Drivers/Input/IInput.cs
index 5ad1f6b690..b5a6f96658 100644
--- a/Terminal.Gui/Drivers/Input/IInput.cs
+++ b/Terminal.Gui/Drivers/Input/IInput.cs
@@ -66,7 +66,7 @@ public interface IInput : IDisposable
///
///
///
- /// This property allows external code (e.g., test harnesses like TestContext) to
+ /// This property allows external code (e.g., test harnesses like AppTestHelper) to
/// provide additional cancellation signals such as timeouts or hard-stop conditions.
///
///
diff --git a/Terminal.Gui/Drivers/UnixDriver/UnixClipboard.cs b/Terminal.Gui/Drivers/UnixDriver/UnixClipboard.cs
index 6562f3b2dc..acfc35b36a 100644
--- a/Terminal.Gui/Drivers/UnixDriver/UnixClipboard.cs
+++ b/Terminal.Gui/Drivers/UnixDriver/UnixClipboard.cs
@@ -8,7 +8,7 @@ namespace Terminal.Gui.Drivers;
internal class UnixClipboard : ClipboardBase
{
private string _xclipPath = string.Empty;
- public UnixClipboard () { IsSupported = CheckSupport (); }
+ public UnixClipboard () => IsSupported = CheckSupport ();
public override bool IsSupported { get; }
protected override string GetClipboardDataImpl ()
diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj
index 765c46305d..544579f229 100644
--- a/Terminal.Gui/Terminal.Gui.csproj
+++ b/Terminal.Gui/Terminal.Gui.csproj
@@ -87,7 +87,7 @@
-
+
diff --git a/Terminal.sln b/Terminal.sln
index ff01131635..74d32e37af 100644
--- a/Terminal.sln
+++ b/Terminal.sln
@@ -113,11 +113,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StressTests", "Tests\Stress
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests.Parallelizable", "Tests\UnitTestsParallelizable\UnitTests.Parallelizable.csproj", "{DE780834-190A-8277-51FD-750CC666E82D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerminalGuiFluentTesting", "Tests\TerminalGuiFluentTesting\TerminalGuiFluentTesting.csproj", "{2DBA7BDC-17AE-474B-A507-00807D087607}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppTestHelpers", "Tests\AppTestHelpers\AppTestHelpers.csproj", "{2DBA7BDC-17AE-474B-A507-00807D087607}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerminalGuiFluentTestingXunit", "Tests\TerminalGuiFluentTestingXunit\TerminalGuiFluentTestingXunit.csproj", "{F56BAFFD-F227-4B0A-96F0-C800FAEF2036}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppTestHelpers.XunitHelpers", "Tests\AppTestHelpers.XunitHelpers\AppTestHelpers.XunitHelpers.csproj", "{F56BAFFD-F227-4B0A-96F0-C800FAEF2036}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerminalGuiFluentTestingXunit.Generator", "Tests\TerminalGuiFluentTestingXunit.Generator\TerminalGuiFluentTestingXunit.Generator.csproj", "{199F27D8-A905-4DDC-82CA-1FE1A90B1788}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppTestHelpers.XunitHelpers.Generator", "Tests\AppTestHelpers.XunitHelpers.Generator\AppTestHelpers.XunitHelpers.Generator.csproj", "{199F27D8-A905-4DDC-82CA-1FE1A90B1788}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{1A3CBA89-EDAB-4F75-811E-FE81B13A4836}"
ProjectSection(SolutionItems) = preProject
diff --git a/Tests/TerminalGuiFluentTestingXunit.Generator/TerminalGuiFluentTestingXunit.Generator.csproj b/Tests/AppTestHelpers.XunitHelpers.Generator/AppTestHelpers.XunitHelpers.Generator.csproj
similarity index 100%
rename from Tests/TerminalGuiFluentTestingXunit.Generator/TerminalGuiFluentTestingXunit.Generator.csproj
rename to Tests/AppTestHelpers.XunitHelpers.Generator/AppTestHelpers.XunitHelpers.Generator.csproj
diff --git a/Tests/TerminalGuiFluentTestingXunit.Generator/TheGenerator.cs b/Tests/AppTestHelpers.XunitHelpers.Generator/TheGenerator.cs
similarity index 93%
rename from Tests/TerminalGuiFluentTestingXunit.Generator/TheGenerator.cs
rename to Tests/AppTestHelpers.XunitHelpers.Generator/TheGenerator.cs
index 45e74fc293..2f4859e6dc 100644
--- a/Tests/TerminalGuiFluentTestingXunit.Generator/TheGenerator.cs
+++ b/Tests/AppTestHelpers.XunitHelpers.Generator/TheGenerator.cs
@@ -4,7 +4,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-namespace TerminalGuiFluentTestingXunit.Generator;
+namespace AppTestHelpers.XunitHelpers.Generator;
[Generator]
public class TheGenerator : IIncrementalGenerator
@@ -89,10 +89,10 @@ private void GenerateMethods (INamedTypeSymbol assertType, SourceProductionConte
var header = """"
#nullable enable
- using TerminalGuiFluentTesting;
+ using AppTestHelpers;
using Xunit;
- namespace TerminalGuiFluentTestingXunit;
+ namespace AppTestHelpers.XunitHelpers;
public static partial class XunitContextExtensions
{
@@ -117,26 +117,26 @@ public static partial class XunitContextExtensions
}
var method = $$"""
- {{signature}}
- {
- try
+ {{signature}}
{
- Assert.{{methodName}}{{typeParams}} ({{string.Join (",", paramNames)}});
- }
- catch(Exception ex)
- {
- context.HardStop (ex);
+ try
+ {
+ Xunit.Assert.{{methodName}}{{typeParams}} ({{string.Join (",", paramNames)}});
+ }
+ catch(Exception ex)
+ {
+ context.HardStop (ex);
+
+
+ throw;
+
+ }
-
- throw;
-
+ return context;
}
-
- return context;
- }
""";
- sb.AppendLine (method);
+ sb.AppendLine (method.Replace ("*", ""));
}
sb.AppendLine (tail);
@@ -154,9 +154,9 @@ out string typeParams
{
typeParams = string.Empty;
- // Create the "this TestContext context" parameter
+ // Create the "this AppTestHelper context" parameter
ParameterSyntax contextParam = SyntaxFactory.Parameter (SyntaxFactory.Identifier ("context"))
- .WithType (SyntaxFactory.ParseTypeName ("TestContext"))
+ .WithType (SyntaxFactory.ParseTypeName ("AppTestHelper"))
.AddModifiers (SyntaxFactory.Token (SyntaxKind.ThisKeyword)); // Add the "this" keyword
// Extract the parameter names (expected and actual)
@@ -182,8 +182,8 @@ out string typeParams
parameters.Insert (0, contextParam); // Insert 'context' as the first parameter
- // Change the return type to TestContext
- TypeSyntax returnType = SyntaxFactory.ParseTypeName ("TestContext");
+ // Change the return type to AppTestHelper
+ TypeSyntax returnType = SyntaxFactory.ParseTypeName ("AppTestHelper");
// Change the method name to AssertEqual
SyntaxToken newMethodName = SyntaxFactory.Identifier ($"Assert{methodName}");
diff --git a/Tests/TerminalGuiFluentTestingXunit/TerminalGuiFluentTestingXunit.csproj b/Tests/AppTestHelpers.XunitHelpers/AppTestHelpers.XunitHelpers.csproj
similarity index 50%
rename from Tests/TerminalGuiFluentTestingXunit/TerminalGuiFluentTestingXunit.csproj
rename to Tests/AppTestHelpers.XunitHelpers/AppTestHelpers.XunitHelpers.csproj
index 31e008633a..6e783b82c5 100644
--- a/Tests/TerminalGuiFluentTestingXunit/TerminalGuiFluentTestingXunit.csproj
+++ b/Tests/AppTestHelpers.XunitHelpers/AppTestHelpers.XunitHelpers.csproj
@@ -1,6 +1,8 @@
+ Exe
+ true
net10.0
enable
enable
@@ -9,11 +11,9 @@
-
-
-
-
-
+
+
+
diff --git a/Tests/TerminalGuiFluentTestingXunit/XunitContextExtensions.cs b/Tests/AppTestHelpers.XunitHelpers/XunitContextExtensions.cs
similarity index 77%
rename from Tests/TerminalGuiFluentTestingXunit/XunitContextExtensions.cs
rename to Tests/AppTestHelpers.XunitHelpers/XunitContextExtensions.cs
index 71755ce035..3318240847 100644
--- a/Tests/TerminalGuiFluentTestingXunit/XunitContextExtensions.cs
+++ b/Tests/AppTestHelpers.XunitHelpers/XunitContextExtensions.cs
@@ -1,8 +1,7 @@
using System.Drawing;
-using TerminalGuiFluentTesting;
using Xunit;
-namespace TerminalGuiFluentTestingXunit;
+namespace AppTestHelpers.XunitHelpers;
public static partial class XunitContextExtensions
{
@@ -15,7 +14,7 @@ public static partial class XunitContextExtensions
///
///
///
- public static TestContext AssertCursorPosition (this TestContext context, Point expected)
+ public static AppTestHelper AssertCursorPosition (this AppTestHelper context, Point expected)
{
try
{
diff --git a/Tests/TerminalGuiFluentTesting/TestContext.ContextMenu.cs b/Tests/AppTestHelpers/AppTestHelper.ContextMenu.cs
similarity index 90%
rename from Tests/TerminalGuiFluentTesting/TestContext.ContextMenu.cs
rename to Tests/AppTestHelpers/AppTestHelper.ContextMenu.cs
index a20cf5ce92..f440f4bb16 100644
--- a/Tests/TerminalGuiFluentTesting/TestContext.ContextMenu.cs
+++ b/Tests/AppTestHelpers/AppTestHelper.ContextMenu.cs
@@ -2,9 +2,9 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
-namespace TerminalGuiFluentTesting;
+namespace AppTestHelpers;
-public partial class TestContext
+public partial class AppTestHelper
{
///
/// Registers a right click handler on the added view (or root view) that
@@ -12,7 +12,7 @@ public partial class TestContext
///
///
///
- public TestContext WithContextMenu (PopoverMenu? contextMenu)
+ public AppTestHelper WithContextMenu (PopoverMenu? contextMenu)
{
if (contextMenu?.App is null)
{
diff --git a/Tests/TerminalGuiFluentTesting/TestContext.Input.cs b/Tests/AppTestHelpers/AppTestHelper.Input.cs
similarity index 89%
rename from Tests/TerminalGuiFluentTesting/TestContext.Input.cs
rename to Tests/AppTestHelpers/AppTestHelper.Input.cs
index 6a151c98f1..5f54506f2e 100644
--- a/Tests/TerminalGuiFluentTesting/TestContext.Input.cs
+++ b/Tests/AppTestHelpers/AppTestHelper.Input.cs
@@ -4,9 +4,9 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
-namespace TerminalGuiFluentTesting;
+namespace AppTestHelpers;
-public partial class TestContext
+public partial class AppTestHelper
{
///
/// Simulates a right click at the given screen coordinates on the current driver.
@@ -16,7 +16,7 @@ public partial class TestContext
/// 0 indexed screen coordinates
/// 0 indexed screen coordinates
///
- public TestContext RightClick (int screenX, int screenY)
+ public AppTestHelper RightClick (int screenX, int screenY)
{
InjectMouseEvent (new Mouse
{
@@ -40,7 +40,7 @@ public TestContext RightClick (int screenX, int screenY)
/// 0 indexed screen coordinates
/// 0 indexed screen coordinates
///
- public TestContext LeftClick (int screenX, int screenY)
+ public AppTestHelper LeftClick (int screenX, int screenY)
{
InjectMouseEvent (new Mouse
{
@@ -63,7 +63,7 @@ public TestContext LeftClick (int screenX, int screenY)
///
///
///
- public TestContext LeftClick (Func evaluator) where TView : View =>
+ public AppTestHelper LeftClick (Func evaluator) where TView : View =>
InjectMouseEvent (new Mouse { Flags = MouseFlags.LeftButtonClicked }, evaluator);
///
@@ -75,8 +75,8 @@ public TestContext LeftClick (Func evaluator) where TView :
/// Whether to advance time after this event to space clicks apart (prevents multi-click
/// detection).
///
- /// This TestContext for fluent chaining.
- private TestContext InjectMouseEvent (Mouse mouse, bool advanceTimeAfter = false)
+ /// This AppTestHelper for fluent chaining.
+ private AppTestHelper InjectMouseEvent (Mouse mouse, bool advanceTimeAfter = false)
{
// Use the new injection infrastructure
WaitIteration (app =>
@@ -116,12 +116,12 @@ private TestContext InjectMouseEvent (Mouse mouse, bool advanceTimeAfter = false
///
/// The mouse event to inject.
/// Function to find the target view.
- /// This TestContext for fluent chaining.
- private TestContext InjectMouseEvent (Mouse mouse, Func evaluator) where TView : View
+ /// This AppTestHelper for fluent chaining.
+ private AppTestHelper InjectMouseEvent (Mouse mouse, Func evaluator) where TView : View
{
var screen = Point.Empty;
- TestContext ctx = WaitIteration (_ =>
+ AppTestHelper ctx = WaitIteration (_ =>
{
TView v = Find (evaluator);
screen = v.ViewportToScreen (new Point (0, 0));
@@ -139,8 +139,8 @@ private TestContext InjectMouseEvent (Mouse mouse, Func eval
/// Uses the new simplified injection API with virtual time support.
///
/// The key to inject.
- /// This TestContext for fluent chaining.
- public TestContext KeyDown (Key key)
+ /// This AppTestHelper for fluent chaining.
+ public AppTestHelper KeyDown (Key key)
{
//Logging.Trace ($"Injecting key: {key}");
diff --git a/Tests/TerminalGuiFluentTesting/TestContext.Navigation.cs b/Tests/AppTestHelpers/AppTestHelper.Navigation.cs
similarity index 93%
rename from Tests/TerminalGuiFluentTesting/TestContext.Navigation.cs
rename to Tests/AppTestHelpers/AppTestHelper.Navigation.cs
index 2e3a82bfc2..27ce8d5c6f 100644
--- a/Tests/TerminalGuiFluentTesting/TestContext.Navigation.cs
+++ b/Tests/AppTestHelpers/AppTestHelper.Navigation.cs
@@ -1,8 +1,8 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
-namespace TerminalGuiFluentTesting;
+namespace AppTestHelpers;
-public partial class TestContext
+public partial class AppTestHelper
{
///
/// Sets the input focus to the given .
@@ -13,7 +13,7 @@ public partial class TestContext
///
///
///
- public TestContext Focus (View toFocus)
+ public AppTestHelper Focus (View toFocus)
{
toFocus.FocusDeepest (NavigationDirection.Forward, TabBehavior.TabStop);
@@ -37,7 +37,7 @@ public TestContext Focus (View toFocus)
///
///
///
- public TestContext Focus (Func? evaluator = null) where T : View
+ public AppTestHelper Focus (Func? evaluator = null) where T : View
{
evaluator ??= _ => true;
View? t = App?.TopRunnableView;
diff --git a/Tests/TerminalGuiFluentTesting/TestContext.ViewBase.cs b/Tests/AppTestHelpers/AppTestHelper.ViewBase.cs
similarity index 95%
rename from Tests/TerminalGuiFluentTesting/TestContext.ViewBase.cs
rename to Tests/AppTestHelpers/AppTestHelper.ViewBase.cs
index 549737ea12..f9d2deeb81 100644
--- a/Tests/TerminalGuiFluentTesting/TestContext.ViewBase.cs
+++ b/Tests/AppTestHelpers/AppTestHelper.ViewBase.cs
@@ -1,8 +1,8 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
-namespace TerminalGuiFluentTesting;
+namespace AppTestHelpers;
-public partial class TestContext
+public partial class AppTestHelper
{
///
/// Adds the given to the current top level view
@@ -10,7 +10,7 @@ public partial class TestContext
///
///
///
- public TestContext Add (View v)
+ public AppTestHelper Add (View v)
{
WaitIteration ((app) =>
{
diff --git a/Tests/TerminalGuiFluentTesting/TestContext.cs b/Tests/AppTestHelpers/AppTestHelper.cs
similarity index 94%
rename from Tests/TerminalGuiFluentTesting/TestContext.cs
rename to Tests/AppTestHelpers/AppTestHelper.cs
index 4814958d62..a7d25954e2 100644
--- a/Tests/TerminalGuiFluentTesting/TestContext.cs
+++ b/Tests/AppTestHelpers/AppTestHelper.cs
@@ -6,13 +6,13 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
-namespace TerminalGuiFluentTesting;
+namespace AppTestHelpers;
///
-/// Fluent API context for testing a Terminal.Gui application. Create
+/// Helper for building integration tests for Terminal.Gui applications. Create
/// an instance using static class.
///
-public partial class TestContext : IDisposable
+public partial class AppTestHelper : IDisposable
{
// ===== Threading & Synchronization =====
private readonly CancellationTokenSource _runCancellationTokenSource = new ();
@@ -60,7 +60,7 @@ public partial class TestContext : IDisposable
/// Constructor for tests that only need Application.Init without running the main loop.
/// Uses the driver's default screen size instead of forcing a specific size.
///
- public TestContext (string driverName, TextWriter? logWriter = null, TimeSpan? timeout = null)
+ public AppTestHelper (string driverName, TextWriter? logWriter = null, TimeSpan? timeout = null)
{
_driverName = driverName;
_logWriter = logWriter;
@@ -110,7 +110,7 @@ public TestContext (string driverName, TextWriter? logWriter = null, TimeSpan? t
///
/// Constructor for tests that need to run the application with Application.Run.
///
- internal TestContext (Func runnableBuilder, int width, int height, string driverName, TextWriter? logWriter = null, TimeSpan? timeout = null)
+ internal AppTestHelper (Func runnableBuilder, int width, int height, string driverName, TextWriter? logWriter = null, TimeSpan? timeout = null)
{
_driverName = driverName;
_logWriter = logWriter;
@@ -283,7 +283,7 @@ private void CommonInit (int width, int height, TimeSpan? timeout)
///
///
///
- public TestContext Then (Action doAction)
+ public AppTestHelper Then (Action doAction)
{
try
{
@@ -307,7 +307,7 @@ public TestContext Then (Action doAction)
///
///
///
- public TestContext WaitIteration (Action? action = null)
+ public AppTestHelper WaitIteration (Action? action = null)
{
// If application has already exited don't wait!
if (Finished || _runCancellationTokenSource.Token.IsCancellationRequested || _ansiInput.ExternalCancellationTokenSource!.Token.IsCancellationRequested)
@@ -355,9 +355,9 @@ public TestContext WaitIteration (Action? action = null)
return this;
}
- public TestContext WaitUntil (Func condition)
+ public AppTestHelper WaitUntil (Func condition)
{
- TestContext? c = null;
+ AppTestHelper? c = null;
var sw = Stopwatch.StartNew ();
//Logging.Trace ($"WaitUntil started with timeout {_timeout}");
@@ -397,9 +397,9 @@ public TestContext WaitUntil (Func condition)
/// new Width for the console.
/// new Height for the console.
///
- public TestContext ResizeConsole (int width, int height) => WaitIteration (app => { app.Driver!.SetScreenSize (width, height); });
+ public AppTestHelper ResizeConsole (int width, int height) => WaitIteration (app => { app.Driver!.SetScreenSize (width, height); });
- public TestContext ScreenShot (string title, TextWriter? writer) =>
+ public AppTestHelper ScreenShot (string title, TextWriter? writer) =>
//Logging.Trace ($"{this.ToIdentifyingString ()}");
WaitIteration (app =>
@@ -410,7 +410,7 @@ public TestContext ScreenShot (string title, TextWriter? writer) =>
writer?.WriteLine (text);
});
- public TestContext AnsiScreenShot (string title, TextWriter? writer) =>
+ public AppTestHelper AnsiScreenShot (string title, TextWriter? writer) =>
//Logging.Trace ($"{this.ToIdentifyingString ()}");
WaitIteration (app =>
@@ -424,7 +424,7 @@ public TestContext AnsiScreenShot (string title, TextWriter? writer) =>
///
/// Stops the application and waits for the background thread to exit.
///
- public TestContext Stop ()
+ public AppTestHelper Stop ()
{
Logging.Trace ($"Stopping application for driver: {_driverName}");
@@ -513,7 +513,7 @@ public void HardStop (Exception? ex = null)
///
///
///
- public TestContext WriteOutLogs (TextWriter? writer)
+ public AppTestHelper WriteOutLogs (TextWriter? writer)
{
if (writer is null)
{
@@ -554,7 +554,7 @@ private void CleanupApplication ()
///
public void Dispose ()
{
- //Logging.Trace ($"Disposing TestContext");
+ //Logging.Trace ($"Disposing AppTestHelper");
Stop ();
var shouldThrow = false;
diff --git a/Tests/TerminalGuiFluentTesting/TerminalGuiFluentTesting.csproj b/Tests/AppTestHelpers/AppTestHelpers.csproj
similarity index 100%
rename from Tests/TerminalGuiFluentTesting/TerminalGuiFluentTesting.csproj
rename to Tests/AppTestHelpers/AppTestHelpers.csproj
diff --git a/Tests/TerminalGuiFluentTesting/AssemblyInfo.cs b/Tests/AppTestHelpers/AssemblyInfo.cs
similarity index 100%
rename from Tests/TerminalGuiFluentTesting/AssemblyInfo.cs
rename to Tests/AppTestHelpers/AssemblyInfo.cs
diff --git a/Tests/TerminalGuiFluentTesting/TestDriver.cs b/Tests/AppTestHelpers/TestDriver.cs
similarity index 93%
rename from Tests/TerminalGuiFluentTesting/TestDriver.cs
rename to Tests/AppTestHelpers/TestDriver.cs
index f67b9e66a1..ac1e25189d 100644
--- a/Tests/TerminalGuiFluentTesting/TestDriver.cs
+++ b/Tests/AppTestHelpers/TestDriver.cs
@@ -1,4 +1,4 @@
-namespace TerminalGuiFluentTesting;
+namespace AppTestHelpers;
///
/// Which driver simulation should be used for testing
diff --git a/Tests/TerminalGuiFluentTesting/TextWriterLogger.cs b/Tests/AppTestHelpers/TextWriterLogger.cs
similarity index 93%
rename from Tests/TerminalGuiFluentTesting/TextWriterLogger.cs
rename to Tests/AppTestHelpers/TextWriterLogger.cs
index 39c39266bb..60dda5f01e 100644
--- a/Tests/TerminalGuiFluentTesting/TextWriterLogger.cs
+++ b/Tests/AppTestHelpers/TextWriterLogger.cs
@@ -1,6 +1,6 @@
using Microsoft.Extensions.Logging;
-namespace TerminalGuiFluentTesting;
+namespace AppTestHelpers;
internal class TextWriterLogger (TextWriter writer) : ILogger
{
diff --git a/Tests/TerminalGuiFluentTesting/TextWriterLoggerProvider.cs b/Tests/AppTestHelpers/TextWriterLoggerProvider.cs
similarity index 88%
rename from Tests/TerminalGuiFluentTesting/TextWriterLoggerProvider.cs
rename to Tests/AppTestHelpers/TextWriterLoggerProvider.cs
index 2a6628329c..d6ea91a0b8 100644
--- a/Tests/TerminalGuiFluentTesting/TextWriterLoggerProvider.cs
+++ b/Tests/AppTestHelpers/TextWriterLoggerProvider.cs
@@ -1,6 +1,6 @@
using Microsoft.Extensions.Logging;
-namespace TerminalGuiFluentTesting;
+namespace AppTestHelpers;
internal class TextWriterLoggerProvider (TextWriter writer) : ILoggerProvider
{
diff --git a/Tests/TerminalGuiFluentTesting/ThreadSafeStringWriter.cs b/Tests/AppTestHelpers/ThreadSafeStringWriter.cs
similarity index 93%
rename from Tests/TerminalGuiFluentTesting/ThreadSafeStringWriter.cs
rename to Tests/AppTestHelpers/ThreadSafeStringWriter.cs
index f9d06743f2..2edebb7282 100644
--- a/Tests/TerminalGuiFluentTesting/ThreadSafeStringWriter.cs
+++ b/Tests/AppTestHelpers/ThreadSafeStringWriter.cs
@@ -1,6 +1,6 @@
using System.Text;
-namespace TerminalGuiFluentTesting;
+namespace AppTestHelpers;
class ThreadSafeStringWriter : StringWriter
{
diff --git a/Tests/TerminalGuiFluentTesting/With.cs b/Tests/AppTestHelpers/With.cs
similarity index 80%
rename from Tests/TerminalGuiFluentTesting/With.cs
rename to Tests/AppTestHelpers/With.cs
index c55384f287..acfeac4274 100644
--- a/Tests/TerminalGuiFluentTesting/With.cs
+++ b/Tests/AppTestHelpers/With.cs
@@ -1,5 +1,5 @@
-namespace TerminalGuiFluentTesting;
+namespace AppTestHelpers;
///
/// Entry point to fluent assertions.
@@ -14,7 +14,7 @@ public static class With
///
///
///
- public static TestContext A (int width, int height, string driverName, TextWriter? logWriter = null) where T : IRunnable, new()
+ public static AppTestHelper A (int width, int height, string driverName, TextWriter? logWriter = null) where T : IRunnable, new()
{
return new (() => new T ()
{
@@ -32,7 +32,7 @@ public static class With
///
///
///
- public static TestContext A (Func runnableFactory, int width, int height, string driverName, TextWriter? logWriter = null)
+ public static AppTestHelper A (Func runnableFactory, int width, int height, string driverName, TextWriter? logWriter = null)
{
return new (runnableFactory, width, height, driverName, logWriter, Timeout);
}
diff --git a/Tests/IntegrationTests/DialogTests.cs b/Tests/IntegrationTests/DialogTests.cs
index e8767f58bc..67ebf4b2bb 100644
--- a/Tests/IntegrationTests/DialogTests.cs
+++ b/Tests/IntegrationTests/DialogTests.cs
@@ -1,7 +1,4 @@
#nullable enable
-using UnitTests;
-using Xunit.Abstractions;
-
namespace IntegrationTests;
public class DialogTests
diff --git a/Tests/IntegrationTests/FluentTests/FileDialogTests.cs b/Tests/IntegrationTests/FluentTests/FileDialogTests.cs
index e456d90bed..9580a79af4 100644
--- a/Tests/IntegrationTests/FluentTests/FileDialogTests.cs
+++ b/Tests/IntegrationTests/FluentTests/FileDialogTests.cs
@@ -3,9 +3,8 @@
using System.IO.Abstractions;
using System.IO.Abstractions.TestingHelpers;
using System.Runtime.InteropServices;
-using TerminalGuiFluentTesting;
-using TerminalGuiFluentTestingXunit;
-using Xunit.Abstractions;
+using AppTestHelpers;
+using AppTestHelpers.XunitHelpers;
namespace IntegrationTests;
@@ -58,7 +57,7 @@ public void CancelFileDialog_QuitKey_Quits (string d)
{
SaveDialog? sd = null;
- using TestContext c = With.A (() => NewSaveDialog (out sd), 100, 20, d, _out)
+ using AppTestHelper c = With.A (() => NewSaveDialog (out sd), 100, 20, d, _out)
.ScreenShot ("Save dialog", _out)
.KeyDown (Application.QuitKey)
.AssertTrue (sd!.Canceled);
@@ -70,7 +69,7 @@ public void CancelFileDialog_UsingCancelButton_TabThenEnter (string d)
{
SaveDialog? sd = null;
- using TestContext c = With.A (() => NewSaveDialog (out sd), 100, 20, d)
+ using AppTestHelper c = With.A (() => NewSaveDialog (out sd), 100, 20, d)
.ScreenShot ("Save dialog", _out)
.Focus