From 24a375676f595a8a1359b344bab9f6fa9bce8125 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Tue, 20 Oct 2026 22:07:03 +0200 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=94=A7=20prepare=20validation=20packa?= =?UTF-8?q?ge=20during=20local=20builds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The validation tests consumed a package that developers had to pack by hand, and stale packages could silently be used. The project now packs Codebelt.Coverlet.MTP into an isolated per-configuration and per-framework directory after each real build, while CI keeps consuming the downloaded release package. --- .../Directory.Build.targets | 13 +++++++++++++ .../coverlet.MTP.validation.tests.csproj | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/test/coverlet.MTP.validation.tests/Directory.Build.targets b/test/coverlet.MTP.validation.tests/Directory.Build.targets index 8a7f9cd7b..a1e6a685f 100644 --- a/test/coverlet.MTP.validation.tests/Directory.Build.targets +++ b/test/coverlet.MTP.validation.tests/Directory.Build.targets @@ -11,4 +11,17 @@ + + + + <_PreviousValidationPackages Include="$(ValidationPackagePath)/Codebelt.Coverlet.MTP.*.nupkg;$(ValidationPackagePath)/Codebelt.Coverlet.MTP.*.snupkg" /> + + + + + + diff --git a/test/coverlet.MTP.validation.tests/coverlet.MTP.validation.tests.csproj b/test/coverlet.MTP.validation.tests/coverlet.MTP.validation.tests.csproj index c0b6cec7e..f1e21ca86 100644 --- a/test/coverlet.MTP.validation.tests/coverlet.MTP.validation.tests.csproj +++ b/test/coverlet.MTP.validation.tests/coverlet.MTP.validation.tests.csproj @@ -10,6 +10,14 @@ true false false + + true + false + true + + $(RepoRoot)artifacts/validation/$(Configuration)/$(TargetFramework) + $(ValidationArtifactsPath)/packages + $(RepoRoot)artifacts/package/$(Configuration.ToLowerInvariant()) true true @@ -26,6 +34,7 @@ + From 671825eb4c2bbd2d74009e6be813ce739e25e749 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Tue, 20 Oct 2026 22:07:05 +0200 Subject: [PATCH 2/5] =?UTF-8?q?=E2=9C=85=20read=20validation=20package=20p?= =?UTF-8?q?ath=20from=20msbuild=20metadata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests hard-coded artifacts/package and asked developers to run dotnet pack, which no longer matches how the package is prepared. They now read the package directory from MSBuild metadata, so local runs and CI each resolve the package that was actually prepared for them. --- .../HelpCommandTests.cs | 15 +++++++-------- .../TestInfrastructure/MtpValidationTestBase.cs | 7 +++---- .../TestInfrastructure/ValidationPackage.cs | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 test/coverlet.MTP.validation.tests/TestInfrastructure/ValidationPackage.cs diff --git a/test/coverlet.MTP.validation.tests/HelpCommandTests.cs b/test/coverlet.MTP.validation.tests/HelpCommandTests.cs index 0be05d10a..9660af06f 100644 --- a/test/coverlet.MTP.validation.tests/HelpCommandTests.cs +++ b/test/coverlet.MTP.validation.tests/HelpCommandTests.cs @@ -45,7 +45,7 @@ public HelpCommandTests() // Get repository root _repoRoot = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..")); - _localPackagesPath = Path.Combine(_repoRoot, "artifacts", "package", _buildConfiguration.ToLowerInvariant()); + _localPackagesPath = ValidationPackage.DirectoryPath; _projectOutputPath = Path.Combine(_repoRoot, "artifacts", "bin", s_projectName, _buildConfiguration.ToLowerInvariant()); @@ -58,21 +58,21 @@ public HelpCommandTests() private protected static string GetPackageVersion(string filter) { - string packagesPath = TestUtils.GetPackagePath(TestUtils.GetAssemblyBuildConfiguration().ToString().ToLowerInvariant()); + string packagesPath = ValidationPackage.DirectoryPath; if (!Directory.Exists(packagesPath)) { - throw new DirectoryNotFoundException($"Package directory '{packagesPath}' not found, run 'dotnet pack' on repository root"); + throw new DirectoryNotFoundException($"Package directory '{packagesPath}' not found. Rebuild the validation test project."); } List files = Directory.GetFiles(packagesPath, filter).ToList(); if (files.Count == 0) { - throw new InvalidOperationException($"Could not find any package using filter '{filter}' in folder '{Path.GetFullPath(packagesPath)}'. Make sure 'dotnet pack' was called."); + throw new InvalidOperationException($"Could not find any package using filter '{filter}' in folder '{Path.GetFullPath(packagesPath)}'. Rebuild the validation test project."); } else if (files.Count > 1) { - throw new InvalidOperationException($"Found more than one package using filter '{filter}' in folder '{Path.GetFullPath(packagesPath)}'. Make sure 'dotnet pack' was only called once."); + throw new InvalidOperationException($"Found more than one package using filter '{filter}' in folder '{Path.GetFullPath(packagesPath)}'. Expected exactly one."); } else { @@ -442,8 +442,7 @@ private async Task EnsureTestProjectBuilt() private void EnsurePackagesBuilt() { - string packagesPath = TestUtils.GetPackagePath( - TestUtils.GetAssemblyBuildConfiguration().ToString().ToLowerInvariant()); + string packagesPath = ValidationPackage.DirectoryPath; // Check for Codebelt.Coverlet.MTP string[] mtpPackages = Directory.GetFiles(packagesPath, "Codebelt.Coverlet.MTP.*.nupkg"); @@ -451,7 +450,7 @@ private void EnsurePackagesBuilt() { throw new InvalidOperationException( $"Codebelt.Coverlet.MTP package not found in '{packagesPath}'.\n" + - $"Run: dotnet pack src/coverlet.MTP -c {_buildConfiguration}"); + "Rebuild the validation test project."); } } diff --git a/test/coverlet.MTP.validation.tests/TestInfrastructure/MtpValidationTestBase.cs b/test/coverlet.MTP.validation.tests/TestInfrastructure/MtpValidationTestBase.cs index 2344bcd27..3b2a6a46a 100644 --- a/test/coverlet.MTP.validation.tests/TestInfrastructure/MtpValidationTestBase.cs +++ b/test/coverlet.MTP.validation.tests/TestInfrastructure/MtpValidationTestBase.cs @@ -30,8 +30,7 @@ protected MtpValidationTestBase() #endif RepoRoot = Path.GetFullPath( Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..")); - LocalPackagesPath = Path.Combine( - RepoRoot, "artifacts", "package", BuildConfiguration.ToLowerInvariant()); + LocalPackagesPath = ValidationPackage.DirectoryPath; } // ── Package version ────────────────────────────────────────────────────── @@ -45,14 +44,14 @@ protected string GetCoverletMtpPackageVersion() if (!Directory.Exists(LocalPackagesPath)) { throw new DirectoryNotFoundException( - $"Local packages path '{LocalPackagesPath}' not found. Run 'dotnet pack' first."); + $"Local packages path '{LocalPackagesPath}' not found. Rebuild the validation test project."); } string[] packages = Directory.GetFiles(LocalPackagesPath, "Codebelt.Coverlet.MTP.*.nupkg"); if (packages.Length == 0) { throw new InvalidOperationException( - $"Could not find Codebelt.Coverlet.MTP package in '{LocalPackagesPath}'. Run 'dotnet pack' first."); + $"Could not find Codebelt.Coverlet.MTP package in '{LocalPackagesPath}'. Rebuild the validation test project."); } if (packages.Length > 1) diff --git a/test/coverlet.MTP.validation.tests/TestInfrastructure/ValidationPackage.cs b/test/coverlet.MTP.validation.tests/TestInfrastructure/ValidationPackage.cs new file mode 100644 index 000000000..2e1c2b909 --- /dev/null +++ b/test/coverlet.MTP.validation.tests/TestInfrastructure/ValidationPackage.cs @@ -0,0 +1,15 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Reflection; + +namespace Coverlet.MTP.validation.tests; + +internal static class ValidationPackage +{ + // MSBuild selects an isolated local package or the downloaded CI release artifact. + internal static string DirectoryPath { get; } = typeof(ValidationPackage).Assembly + .GetCustomAttributes() + .Single(attribute => attribute.Key == "ValidationPackagePath").Value + ?? throw new InvalidOperationException("Validation package path is missing. Rebuild the validation test project."); +} From d3f2391d5c0c0b413f3ecd1998e54ada49e05146 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Tue, 20 Oct 2026 22:07:06 +0200 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=93=9D=20document=20automatic=20valid?= =?UTF-8?q?ation=20package=20packing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The contributing guide told developers to run dotnet pack before the validation tests, which is no longer required. It now explains that local builds pack the package automatically, that CI uses the downloaded release package, and how to opt out. --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 630f27014..25a43ee1f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,9 @@ dotnet pack src/coverlet.MTP/coverlet.MTP.csproj -c Release --no-build dotnet test -c Release --no-build ``` -The MTP package-validation tests consume the locally packed `Codebelt.Coverlet.MTP` package from `artifacts/package/release`, so pack before running `test/coverlet.MTP.validation.tests` directly. +The MTP package-validation project automatically packs `Codebelt.Coverlet.MTP` when built locally, including builds started by Visual Studio Test Explorer. No manual packing or package-folder cleanup is required. Each configuration and test framework uses an isolated directory under `artifacts/validation`, so older packages in `artifacts/package` do not affect local validation. The first build takes longer because it builds the package for all supported frameworks; subsequent package builds reuse their build outputs. + +In GitHub Actions, automatic packing is disabled so validation consumes the downloaded release package from `artifacts/package/release`. Outside GitHub Actions, use `-p:PrepareValidationPackage=false` to validate an existing package from `artifacts/package/` instead. That directory must contain exactly one `Codebelt.Coverlet.MTP.*.nupkg`. As usual, `dotnet test --no-build` reuses the package prepared by the previous build. ## Focused test runs From 1f2802946fe2648a7cdf7e19b68eef025a88d8ec Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Tue, 20 Oct 2026 22:17:42 +0200 Subject: [PATCH 4/5] =?UTF-8?q?=E2=9C=85=20clarify=20validation=20package?= =?UTF-8?q?=20missing=20guidance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The validation tests reported a missing Codebelt.Coverlet.MTP package with only a rebuild hint. The messages now name the existing package and the -p:PrepareValidationPackage=true option so developers know how to supply or prepare it. --- test/coverlet.MTP.validation.tests/HelpCommandTests.cs | 6 +++--- .../TestInfrastructure/MtpValidationTestBase.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/coverlet.MTP.validation.tests/HelpCommandTests.cs b/test/coverlet.MTP.validation.tests/HelpCommandTests.cs index 9660af06f..7cdc514d5 100644 --- a/test/coverlet.MTP.validation.tests/HelpCommandTests.cs +++ b/test/coverlet.MTP.validation.tests/HelpCommandTests.cs @@ -62,13 +62,13 @@ private protected static string GetPackageVersion(string filter) if (!Directory.Exists(packagesPath)) { - throw new DirectoryNotFoundException($"Package directory '{packagesPath}' not found. Rebuild the validation test project."); + throw new DirectoryNotFoundException($"Package directory '{packagesPath}' not found. Supply the existing Codebelt.Coverlet.MTP package in this directory, or rebuild the validation test project locally with -p:PrepareValidationPackage=true to prepare it automatically."); } List files = Directory.GetFiles(packagesPath, filter).ToList(); if (files.Count == 0) { - throw new InvalidOperationException($"Could not find any package using filter '{filter}' in folder '{Path.GetFullPath(packagesPath)}'. Rebuild the validation test project."); + throw new InvalidOperationException($"Could not find any package using filter '{filter}' in folder '{Path.GetFullPath(packagesPath)}'. Supply the existing Codebelt.Coverlet.MTP package in this directory, or rebuild the validation test project locally with -p:PrepareValidationPackage=true to prepare it automatically."); } else if (files.Count > 1) { @@ -450,7 +450,7 @@ private void EnsurePackagesBuilt() { throw new InvalidOperationException( $"Codebelt.Coverlet.MTP package not found in '{packagesPath}'.\n" + - "Rebuild the validation test project."); + "Supply the existing Codebelt.Coverlet.MTP package in this directory, or rebuild the validation test project locally with -p:PrepareValidationPackage=true to prepare it automatically."); } } diff --git a/test/coverlet.MTP.validation.tests/TestInfrastructure/MtpValidationTestBase.cs b/test/coverlet.MTP.validation.tests/TestInfrastructure/MtpValidationTestBase.cs index 3b2a6a46a..852f3454f 100644 --- a/test/coverlet.MTP.validation.tests/TestInfrastructure/MtpValidationTestBase.cs +++ b/test/coverlet.MTP.validation.tests/TestInfrastructure/MtpValidationTestBase.cs @@ -44,14 +44,14 @@ protected string GetCoverletMtpPackageVersion() if (!Directory.Exists(LocalPackagesPath)) { throw new DirectoryNotFoundException( - $"Local packages path '{LocalPackagesPath}' not found. Rebuild the validation test project."); + $"Local packages path '{LocalPackagesPath}' not found. Supply the existing Codebelt.Coverlet.MTP package in this directory, or rebuild the validation test project locally with -p:PrepareValidationPackage=true to prepare it automatically."); } string[] packages = Directory.GetFiles(LocalPackagesPath, "Codebelt.Coverlet.MTP.*.nupkg"); if (packages.Length == 0) { throw new InvalidOperationException( - $"Could not find Codebelt.Coverlet.MTP package in '{LocalPackagesPath}'. Rebuild the validation test project."); + $"Could not find Codebelt.Coverlet.MTP package in '{LocalPackagesPath}'. Supply the existing Codebelt.Coverlet.MTP package in this directory, or rebuild the validation test project locally with -p:PrepareValidationPackage=true to prepare it automatically."); } if (packages.Length > 1) From 2bb1c90978ffaf204134ca32f3c0e1edd1ae80f3 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 30 Oct 2026 08:24:26 +0100 Subject: [PATCH 5/5] changelog --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aa74aa89..f079195bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ 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). +## [vNext] - 2026-10-30 + +This is a patch release that makes the MTP package-validation tests prepare their own Codebelt.Coverlet.MTP package during local builds, including Visual Studio Test Explorer runs, with no runtime or package behavior changes. + +### Added + +- Add automatic preparation of the Codebelt.Coverlet.MTP package for the MTP package-validation tests during local builds, skipped for IDE design-time builds, with isolated output under `artifacts/validation` per configuration and framework. + +### Changed + +- Change the validation tests to read the package folder from MSBuild metadata instead of a hard-coded `artifacts/package/` path, with missing-package guidance that points to `-p:PrepareValidationPackage=true` or an existing package, +- Disable automatic package preparation in GitHub Actions so validation consumes the downloaded release package, while allowing opt-out outside GitHub Actions with `-p:PrepareValidationPackage=false`, +- Document the automatic packing behavior and opt-out in `CONTRIBUTING.md`, replacing the manual `dotnet pack` step. + ## [10.1.1] - 2026-10-20 This is a patch release that fixes module unload mutex collisions between unrelated coverage sessions and updates the Microsoft runtime pins and the `System.Security.Cryptography.Pkcs` override used by coverlet.core, with no new capabilities. @@ -97,6 +111,7 @@ This is a minor release focused on the Codebelt.Coverlet.MTP fork with narrowed - .vscode/settings.json configuration, - .devcontainer legacy configuration. +[10.1.2]: https://github.com/codebeltnet/coverlet/compare/v10.1.1...vNext [10.1.1]: https://github.com/codebeltnet/coverlet/compare/v10.1.0...v10.1.1 [10.1.0]: https://github.com/codebeltnet/coverlet/compare/v10.0.1...v10.1.0 [10.0.1]: https://github.com/codebeltnet/coverlet/compare/v10.0.0...v10.0.1