diff --git a/CHANGELOG.md b/CHANGELOG.md
index 046a2d0..0738cf6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.0.8] - 2026-08-18
+
+### Changed
+- Migrated tests to xunit.v3 `4.0` and Microsoft Testing Platform only (`src/global.json` `test.runner` next to the `.slnx`; no VSTest, `coverlet.collector`, `Microsoft.NET.Test.Sdk`, or `xunit.runner.visualstudio`). Coverage now uses `coverlet.MTP`; TestRunner always passes `--coverlet` scoped to `[MaksIT.*]*`.
+
## [1.0.7] - 2026-08-14
### Changed
diff --git a/README.md b/README.md
index d68a466..48590d6 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,9 @@
Reusable high-availability runtime coordination library for MaksIT services.
-
-
-
+
+
+
## Packages
diff --git a/src/MaksIT.HAMode.Tests/MaksIT.HAMode.Tests.csproj b/src/MaksIT.HAMode.Tests/MaksIT.HAMode.Tests.csproj
index b8341f5..58ffeed 100644
--- a/src/MaksIT.HAMode.Tests/MaksIT.HAMode.Tests.csproj
+++ b/src/MaksIT.HAMode.Tests/MaksIT.HAMode.Tests.csproj
@@ -6,21 +6,15 @@
enable
false
true
+ Exe
+ true
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
diff --git a/src/MaksIT.HAMode/MaksIT.HAMode.csproj b/src/MaksIT.HAMode/MaksIT.HAMode.csproj
index e0e81a3..b8368fe 100644
--- a/src/MaksIT.HAMode/MaksIT.HAMode.csproj
+++ b/src/MaksIT.HAMode/MaksIT.HAMode.csproj
@@ -8,7 +8,7 @@
$(NoWarn);CS1591
MaksIT.HAMode
- 1.0.7
+ 1.0.8
Maksym Sadovnychyy
MAKS-IT
MaksIT.HAMode
diff --git a/src/global.json b/src/global.json
new file mode 100644
index 0000000..3140116
--- /dev/null
+++ b/src/global.json
@@ -0,0 +1,5 @@
+{
+ "test": {
+ "runner": "Microsoft.Testing.Platform"
+ }
+}
diff --git a/utils/modules/TestRunner.psm1 b/utils/modules/TestRunner.psm1
index aac3308..d1e2eea 100644
--- a/utils/modules/TestRunner.psm1
+++ b/utils/modules/TestRunner.psm1
@@ -7,7 +7,7 @@
.DESCRIPTION
Provides the Invoke-TestsWithCoverage function for running .NET tests
- with Coverlet code coverage collection and parsing results.
+ with Microsoft.Testing.Platform and coverlet.MTP code coverage.
.NOTES
Author: MaksIT
@@ -65,6 +65,20 @@ function Write-TestRunnerLogInternal {
Write-Host $Message -ForegroundColor Gray
}
+function Get-CoberturaCoverageFiles {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$ResultsDirectory
+ )
+
+ # coverlet.MTP: [prefix.]coverage.cobertura[.timestamp].xml
+ $files = @(
+ Get-ChildItem -Path $ResultsDirectory -Recurse -File -ErrorAction SilentlyContinue |
+ Where-Object { $_.Name -like '*coverage.cobertura*.xml' }
+ )
+ return @($files | Sort-Object FullName -Unique)
+}
+
function Invoke-TestsWithCoverage {
<#
.SYNOPSIS
@@ -155,7 +169,7 @@ function Invoke-TestsWithCoverage {
New-Item -ItemType Directory -Path $ResultsDir -Force | Out-Null
if (-not $Silent) {
- Write-TestRunnerLogInternal -Level "STEP" -Message "Running tests with code coverage..."
+ Write-TestRunnerLogInternal -Level "STEP" -Message "Running tests with code coverage (Microsoft.Testing.Platform / coverlet.MTP)..."
foreach ($d in $resolvedProjectDirs) {
Write-TestRunnerLogInternal -Level "INFO" -Message "Test Project: $d"
}
@@ -164,11 +178,15 @@ function Invoke-TestsWithCoverage {
foreach ($TestProjectDir in $resolvedProjectDirs) {
Push-Location $TestProjectDir
try {
+ $projectName = [System.IO.Path]::GetFileName($TestProjectDir)
$dotnetArgs = @(
"test"
- "--collect:XPlat Code Coverage"
"--results-directory", $ResultsDir
"--verbosity", $(if ($Silent) { "quiet" } else { "normal" })
+ "--coverlet"
+ "--coverlet-output-format", "cobertura"
+ "--coverlet-file-prefix", $projectName
+ "--coverlet-include", "[MaksIT.*]*"
)
Import-ExternalCommandSupportInternal
@@ -192,7 +210,7 @@ function Invoke-TestsWithCoverage {
}
}
- $coverageFiles = @(Get-ChildItem -Path $ResultsDir -Filter "coverage.cobertura.xml" -Recurse | Sort-Object FullName)
+ $coverageFiles = @(Get-CoberturaCoverageFiles -ResultsDirectory $ResultsDir)
if ($coverageFiles.Count -eq 0) {
return [PSCustomObject]@{
@@ -455,7 +473,7 @@ function Get-DotNetCoverageFromResultsDirectory {
[switch]$Silent
)
- $coverageFiles = @(Get-ChildItem -Path $ResultsDirectory -Filter 'coverage.cobertura.xml' -Recurse -ErrorAction SilentlyContinue | Sort-Object FullName)
+ $coverageFiles = @(Get-CoberturaCoverageFiles -ResultsDirectory $ResultsDirectory)
if ($coverageFiles.Count -eq 0) {
return [PSCustomObject]@{
Success = $false
@@ -566,7 +584,7 @@ function Get-CoverageFromResultsDirectory {
}
}
- $hasDotNet = @(Get-ChildItem -Path $resolvedDirectory -Filter 'coverage.cobertura.xml' -Recurse -ErrorAction SilentlyContinue).Count -gt 0
+ $hasDotNet = @(Get-CoberturaCoverageFiles -ResultsDirectory $resolvedDirectory).Count -gt 0
$jestSummary = Join-Path $resolvedDirectory 'coverage-summary.json'
$hasNpm = Test-Path -LiteralPath $jestSummary -PathType Leaf
diff --git a/utils/plugins/DotNet/DotNetTest.psm1 b/utils/plugins/DotNet/DotNetTest.psm1
index b8bd09d..3aa8db9 100644
--- a/utils/plugins/DotNet/DotNetTest.psm1
+++ b/utils/plugins/DotNet/DotNetTest.psm1
@@ -7,12 +7,15 @@
.DESCRIPTION
Resolves one or more .NET test projects (`project` or `projects`), runs tests once
- via TestRunner, then publishes metrics on the shared engine context for any later
+ via TestRunner (Microsoft Testing Platform + coverlet.MTP only — no VSTest /
+ coverlet.collector), then publishes metrics on the shared engine context for any later
plugin: `qualityLineCoverage`, `testResult`, `coverageLineRate` / `coverageBranchRate` / `coverageMethodRate`,
method counts, `testResultsDirectory`, `coverageCoberturaPaths`. Quality gates read
those keys generically (not tied to this plugin by name). When `resultsDir` (or the
multi-project default TestResults folder) is used, Cobertura output is kept on disk
via TestRunner `-KeepResults` so repo-root `test-results/` persists after the run.
+ Product solutions must place `src/global.json` with `test.runner` =
+ Microsoft.Testing.Platform next to the `.sln`/`.slnx`.
#>
if (-not (Get-Command Import-PluginDependency -ErrorAction SilentlyContinue)) {