From a8057d9ede7c4e114cd22145ca3724e2e9217128 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Tue, 16 Jun 2026 17:36:11 +0200 Subject: [PATCH 01/20] feat: added tests for legacy parameter syntax --- .../DotCover/Cover/DotCoverCovererTests.cs | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs index f98ab9b6f9..229deb442b 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs @@ -87,6 +87,8 @@ public void Should_Throw_If_No_Tool_Was_Intercepted() AssertEx.IsCakeException(result, "No tool was started."); } + #region New Paramter Syntax + [Fact] public void Should_Capture_Tool_And_Arguments_From_Action() { @@ -534,6 +536,228 @@ public void Should_Support_New_Features_In_New_Mode() Assert.Contains("--exclude-assemblies \"*.Tests\"", result.Args); Assert.Contains("--snapshot-output", result.Args); } + + #endregion + + #region Legacy Paramter Syntax + + [Fact] + public void Should_Capture_Tool_And_Arguments_From_Action_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\"", result.Args); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + public void Should_Not_Capture_Arguments_From_Action_If_Excluded_LegacySyntax(string arguments) + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Action = context => + { + context.ProcessRunner.Start( + new FilePath("/Working/tools/Test.exe"), + new ProcessSettings() + { + Arguments = arguments + }); + }; + fixture.Settings.WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/Output=\"/Working/result.dcvr\"", result.Args); + } + + [Fact] + public void Should_Append_TargetWorkingDir_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.TargetWorkingDir = new DirectoryPath("/Working"); + fixture.Settings.WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\" " + + "/TargetWorkingDir=\"/Working\"", result.Args); + } + + [Fact] + public void Should_Append_Scope_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.WithScope("/Working/*.dll") + .WithScope("/Some/**/Other/*.dll") + .WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\" " + + "/Scope=\"/Working/*.dll;/Some/**/Other/*.dll\"", result.Args); + } + + [Fact] + public void Should_Append_Filters_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.WithFilter("+:module=Test.*") + .WithFilter("-:myassembly") + .WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\" " + + "/Filters=\"+:module=Test.*;-:myassembly\"", result.Args); + } + + [Fact] + public void Should_Append_AttributeFilters_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.WithAttributeFilter("filter1") + .WithAttributeFilter("filter2") + .WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\" " + + "/AttributeFilters=\"filter1;filter2\"", result.Args); + } + + [Fact] + public void Should_Append_DisableDefaultFilters_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.DisableDefaultFilters = true; + fixture.Settings.WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\" " + + "/DisableDefaultFilters", result.Args); + } + + [Fact] + public void Should_Append_ProcessFilters_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.WithProcessFilter("+:test.exe") + .WithProcessFilter("-:sqlservr.exe") + .WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\" " + + "/ProcessFilters=\"+:test.exe;-:sqlservr.exe\"", result.Args); + } + + [Fact] + public void Should_Capture_XUnit_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.FileSystem.CreateFile("/Working/tools/xunit.console.exe"); + fixture.Action = context => + { + context.XUnit2( + new FilePath[] { "./Test.dll" }, + new XUnit2Settings { ShadowCopy = false }); + }; + fixture.Settings.WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/xunit.console.exe\" " + + "/TargetArguments=\"\\\"/Working/Test.dll\\\" -noshadow\" " + + "/Output=\"/Working/result.dcvr\"", result.Args); + } + + [Fact] + public void Should_Capture_NUnit_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.FileSystem.CreateFile("/Working/tools/nunit-console.exe"); + fixture.Action = context => + { + context.NUnit( + new FilePath[] { "./Test.dll" }, + new NUnitSettings { ShadowCopy = false }); + }; + fixture.Settings.WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/nunit-console.exe\" " + + "/TargetArguments=\"\\\"/Working/Test.dll\\\" -noshadow\" " + + "/Output=\"/Working/result.dcvr\"", result.Args); + } + + [Fact] + public void Should_Append_ConfigurationFile_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.WithConfigFile(new FilePath("./config.xml")).WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover \"/Working/config.xml\" /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\"", result.Args); + } + + #endregion } } } \ No newline at end of file From b229df5307f28d1f5c6ecbd278776818e999f8b1 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Tue, 16 Jun 2026 17:37:02 +0200 Subject: [PATCH 02/20] feat: moved UseLegacySyntax property to DotCoverCoverSettings --- .../Tools/DotCover/Cover/DotCoverCoverSettings.cs | 8 -------- src/Cake.Common/Tools/DotCover/DotCoverSettings.cs | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverSettings.cs b/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverSettings.cs index eadc7ce291..66d8d884e6 100644 --- a/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverSettings.cs +++ b/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverSettings.cs @@ -52,13 +52,5 @@ public sealed class DotCoverCoverSettings : DotCoverCoverageSettings /// This represents the --no-ngen option. /// public bool NoNGen { get; set; } - - /// - /// Gets or sets a value indicating whether to use the legacy command syntax. - /// When true, uses old format like '/TargetExecutable="/path"'. - /// When false, uses new format like '--target-executable "/path"'. - /// Default is false (new format). - /// - public bool UseLegacySyntax { get; set; } } } \ No newline at end of file diff --git a/src/Cake.Common/Tools/DotCover/DotCoverSettings.cs b/src/Cake.Common/Tools/DotCover/DotCoverSettings.cs index c1e67be183..e5edf53d91 100644 --- a/src/Cake.Common/Tools/DotCover/DotCoverSettings.cs +++ b/src/Cake.Common/Tools/DotCover/DotCoverSettings.cs @@ -24,5 +24,13 @@ public abstract class DotCoverSettings : ToolSettings /// to specifying all parameters in-line or having them in a batch file. /// public FilePath ConfigFile { get; set; } + + /// + /// Gets or sets a value indicating whether to use the legacy command syntax. + /// When true, uses old format like '/TargetExecutable="/path"'. + /// When false, uses new format like '--target-executable "/path"'. + /// Default is false (new format). + /// + public bool UseLegacySyntax { get; set; } } } From 2605cfbefc5c31c1ff033172be6fbf289dde4d57 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Tue, 16 Jun 2026 17:48:21 +0200 Subject: [PATCH 03/20] fix: removed legacy parameters from new GetCoverCoverageArguments fnc --- .../DotCover/Cover/DotCoverCovererTests.cs | 46 ++++++++----------- .../Tools/DotCover/Cover/DotCoverCoverer.cs | 35 -------------- 2 files changed, 20 insertions(+), 61 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs index 229deb442b..5f76af5c60 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs @@ -9,7 +9,6 @@ using Cake.Common.Tools.XUnit; using Cake.Core.IO; using Cake.Testing; -using Xunit; namespace Cake.Common.Tests.Unit.Tools.DotCover.Cover { @@ -147,12 +146,12 @@ public void Should_Append_TargetWorkingDir() } [Fact] - public void Should_Append_Scope() + public void Should_Not_Append_Scope() { // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithScope("/Working/*.dll") - .WithScope("/Some/**/Other/*.dll"); + .WithScope("/Some/**/Other/*.dll"); // When var result = fixture.Run(); @@ -160,17 +159,16 @@ public void Should_Append_Scope() // Then Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + - "--snapshot-output \"/Working/result.dcvr\" " + - "/Scope=\"/Working/*.dll;/Some/**/Other/*.dll\"", result.Args); + "--snapshot-output \"/Working/result.dcvr\"", result.Args); } [Fact] - public void Should_Append_Filters() + public void Should_Not_Append_Filters() { // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithFilter("+:module=Test.*") - .WithFilter("-:myassembly"); + .WithFilter("-:myassembly"); // When var result = fixture.Run(); @@ -178,17 +176,16 @@ public void Should_Append_Filters() // Then Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + - "--snapshot-output \"/Working/result.dcvr\" " + - "/Filters=\"+:module=Test.*;-:myassembly\"", result.Args); + "--snapshot-output \"/Working/result.dcvr\"", result.Args); } [Fact] - public void Should_Append_AttributeFilters() + public void Should_Not_Append_AttributeFilters() { // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithAttributeFilter("filter1") - .WithAttributeFilter("filter2"); + .WithAttributeFilter("filter2"); // When var result = fixture.Run(); @@ -196,12 +193,11 @@ public void Should_Append_AttributeFilters() // Then Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + - "--snapshot-output \"/Working/result.dcvr\" " + - "/AttributeFilters=\"filter1;filter2\"", result.Args); + "--snapshot-output \"/Working/result.dcvr\"", result.Args); } [Fact] - public void Should_Append_DisableDefaultFilters() + public void Should_Not_Append_DisableDefaultFilters() { // Given var fixture = new DotCoverCovererFixture(); @@ -213,17 +209,16 @@ public void Should_Append_DisableDefaultFilters() // Then Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + - "--snapshot-output \"/Working/result.dcvr\" " + - "/DisableDefaultFilters", result.Args); + "--snapshot-output \"/Working/result.dcvr\"", result.Args); } [Fact] - public void Should_Append_ProcessFilters() + public void Should_Not_Append_ProcessFilters() { // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithProcessFilter("+:test.exe") - .WithProcessFilter("-:sqlservr.exe"); + .WithProcessFilter("-:sqlservr.exe"); // When var result = fixture.Run(); @@ -231,8 +226,7 @@ public void Should_Append_ProcessFilters() // Then Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + - "--snapshot-output \"/Working/result.dcvr\" " + - "/ProcessFilters=\"+:test.exe;-:sqlservr.exe\"", result.Args); + "--snapshot-output \"/Working/result.dcvr\"", result.Args); } [Fact] @@ -301,7 +295,7 @@ public void Should_Append_ExcludeAssemblies() // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithExcludeAssembly("*.Tests") - .WithExcludeAssembly("Test.*"); + .WithExcludeAssembly("Test.*"); // When var result = fixture.Run(); @@ -319,7 +313,7 @@ public void Should_Append_ExcludeAttributes() // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithExcludeAttribute("System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute") - .WithExcludeAttribute("Custom.*Attribute"); + .WithExcludeAttribute("Custom.*Attribute"); // When var result = fixture.Run(); @@ -337,7 +331,7 @@ public void Should_Append_ExcludeProcesses() // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithExcludeProcess("test.exe") - .WithExcludeProcess("*.vshost.exe"); + .WithExcludeProcess("*.vshost.exe"); // When var result = fixture.Run(); @@ -506,8 +500,8 @@ public void Should_Not_Support_New_Features_In_Legacy_Mode() // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithLegacySyntax() - .WithJsonReportOutput(new FilePath("/Working/report.json")) - .WithExcludeAssembly("*.Tests"); + .WithJsonReportOutput(new FilePath("/Working/report.json")) + .WithExcludeAssembly("*.Tests"); // When var result = fixture.Run(); @@ -526,7 +520,7 @@ public void Should_Support_New_Features_In_New_Mode() // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithJsonReportOutput(new FilePath("/Working/report.json")) - .WithExcludeAssembly("*.Tests"); + .WithExcludeAssembly("*.Tests"); // When var result = fixture.Run(); diff --git a/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs b/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs index c3d53ab657..96e5339908 100644 --- a/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs +++ b/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs @@ -165,41 +165,6 @@ private ProcessArgumentBuilder GetCoverCoverageArguments(DotCoverCoverageSetting builder.AppendSwitch("--exclude-processes", excludeProcesses.Quote()); } - // Legacy filtering options (maintain backward compatibility with old format) - // Scope - if (settings.Scope.Count > 0) - { - var scope = string.Join(';', settings.Scope); - builder.AppendSwitch("/Scope", "=", scope.Quote()); - } - - // Filters - if (settings.Filters.Count > 0) - { - var filters = string.Join(';', settings.Filters); - builder.AppendSwitch("/Filters", "=", filters.Quote()); - } - - // AttributeFilters - if (settings.AttributeFilters.Count > 0) - { - var attributeFilters = string.Join(';', settings.AttributeFilters); - builder.AppendSwitch("/AttributeFilters", "=", attributeFilters.Quote()); - } - - // ProcessFilters - if (settings.ProcessFilters.Count > 0) - { - var processFilters = string.Join(';', settings.ProcessFilters); - builder.AppendSwitch("/ProcessFilters", "=", processFilters.Quote()); - } - - // DisableDefaultFilters - if (settings.DisableDefaultFilters) - { - builder.Append("/DisableDefaultFilters"); - } - return builder; } From 8e770b62562d4da0335b2e5c740079fb05d0a0d8 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Wed, 17 Jun 2026 07:50:09 +0200 Subject: [PATCH 04/20] feat: implemented new parameter syntax in DotCoverMerger --- .../DotCover/Merge/DotCoverMergerTests.cs | 82 ++++++++++++++++++- .../Tools/DotCover/DotCoverTool.cs | 12 ++- .../DotCover/Merge/DotCoverMergeSettings.cs | 7 ++ .../Tools/DotCover/Merge/DotCoverMerger.cs | 43 ++++++++-- 4 files changed, 132 insertions(+), 12 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs index 238584413e..e0767b44c5 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs @@ -2,11 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using Cake.Common.Tests.Fixtures.Tools.DotCover.Merge; using Cake.Common.Tools.DotCover; using Cake.Core.IO; -using Xunit; namespace Cake.Common.Tests.Unit.Tools.DotCover.Merge { @@ -70,6 +68,57 @@ public void Should_Throw_If_Settings_Are_Null() AssertEx.IsArgumentNullException(result, "settings"); } + #region New Parameter Syntax + + [Fact] + public void Should_Set_Correct_Arguments() + { + // Given + var fixture = new DotCoverMergerFixture(); + fixture.SourceFiles = new List { new ("/Working/result1.dcvr"), new ("/Working/result2.dcvr") }; + fixture.OutputFile = new FilePath("/Working/output.dcvr"); + // When + var result = fixture.Run(); + + // Then + Assert.Equal("merge " + + "--snapshot-source \"/Working/result1.dcvr,/Working/result2.dcvr\" " + + "--snapshot-output \"/Working/output.dcvr\"", result.Args); + } + + [Fact] + public void Should_Append_TemporaryDirectory() + { + // Given + var fixture = new DotCoverMergerFixture(); + fixture.Settings.TemporaryDirectory = new DirectoryPath("/Working/temp"); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("merge " + + "--snapshot-source \"/Working/result1.dcvr,/Working/result2.dcvr\" " + + "--snapshot-output \"/Working/result.dcvr\" " + + "--temporary-directory \"/Working/temp\"", result.Args); + } + + [Fact] + public void Should_Not_Append_Null_TemporaryDirectory() + { + // Given + var fixture = new DotCoverMergerFixture(); + fixture.Settings.TemporaryDirectory = null; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("merge " + + "--snapshot-source \"/Working/result1.dcvr,/Working/result2.dcvr\" " + + "--snapshot-output \"/Working/result.dcvr\"", result.Args); + } + [Fact] public void Should_Append_LogFile() { @@ -81,7 +130,29 @@ public void Should_Append_LogFile() var result = fixture.Run(); // Then - Assert.Equal("Merge " + + Assert.Equal("merge " + + "--snapshot-source \"/Working/result1.dcvr,/Working/result2.dcvr\" " + + "--snapshot-output \"/Working/result.dcvr\" " + + "--log-file \"/Working/logfile.log\"", result.Args); + } + + #endregion + + #region Legacy Parameter Syntax + + [Fact] + public void Should_Append_LogFile_LegacySyntax() + { + // Given + var fixture = new DotCoverMergerFixture(); + fixture.Settings.LogFile = "./logfile.log"; + fixture.Settings.UseLegacySyntax = true; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("merge " + "/Source=\"/Working/result1.dcvr;/Working/result2.dcvr\" " + "/Output=\"/Working/result.dcvr\" " + "/LogFile=\"/Working/logfile.log\"", result.Args); @@ -93,15 +164,18 @@ public void Should_Append_ConfigurationFile() // Given var fixture = new DotCoverMergerFixture(); fixture.Settings.WithConfigFile(new FilePath("./config.xml")); + fixture.Settings.UseLegacySyntax = true; // When var result = fixture.Run(); // Then - Assert.Equal("Merge \"/Working/config.xml\" " + + Assert.Equal("merge \"/Working/config.xml\" " + "/Source=\"/Working/result1.dcvr;/Working/result2.dcvr\" " + "/Output=\"/Working/result.dcvr\"", result.Args); } + + #endregion } } } \ No newline at end of file diff --git a/src/Cake.Common/Tools/DotCover/DotCoverTool.cs b/src/Cake.Common/Tools/DotCover/DotCoverTool.cs index 1293f35111..08ec603199 100644 --- a/src/Cake.Common/Tools/DotCover/DotCoverTool.cs +++ b/src/Cake.Common/Tools/DotCover/DotCoverTool.cs @@ -64,8 +64,16 @@ protected ProcessArgumentBuilder GetArguments(DotCoverSettings settings) // LogFile if (settings.LogFile != null) { - var logFilePath = settings.LogFile.MakeAbsolute(_environment); - builder.AppendSwitch("/LogFile", "=", logFilePath.FullPath.Quote()); + if (settings.UseLegacySyntax) + { + var logFilePath = settings.LogFile.MakeAbsolute(_environment); + builder.AppendSwitch("/LogFile", "=", logFilePath.FullPath.Quote()); + } + else + { + var logFilePath = settings.LogFile.MakeAbsolute(_environment); + builder.AppendSwitch("--log-file", logFilePath.FullPath.Quote()); + } } return builder; diff --git a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMergeSettings.cs b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMergeSettings.cs index 090de38cfa..ad95f89609 100644 --- a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMergeSettings.cs +++ b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMergeSettings.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Cake.Core.IO; + namespace Cake.Common.Tools.DotCover.Merge { /// @@ -9,5 +11,10 @@ namespace Cake.Common.Tools.DotCover.Merge /// public sealed class DotCoverMergeSettings : DotCoverSettings { + /// + /// Gets or sets the directory for temporary files. + /// This represents the --temporary-directory option. + /// + public DirectoryPath TemporaryDirectory { get; set; } } } diff --git a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs index 36f05ede9e..b480e5fc5b 100644 --- a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs +++ b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs @@ -63,11 +63,47 @@ private ProcessArgumentBuilder GetArguments( { var builder = new ProcessArgumentBuilder(); - builder.Append("Merge"); + // Command name - always lowercase 'merge' for both formats + builder.Append("merge"); // Set configuration file if exists. GetConfigurationFileArgument(settings).CopyTo(builder); + if (settings.UseLegacySyntax) + { + BuildLegacyArguments(sourceFiles, outputFile, builder); + } + else + { + BuildNewArguments(sourceFiles, outputFile, builder, settings); + } + + // Get Global settings + GetArguments(settings).CopyTo(builder); + + return builder; + } + + private void BuildNewArguments(IEnumerable sourceFiles, FilePath outputFile, ProcessArgumentBuilder builder, DotCoverMergeSettings settings) + { + // Set the Source files. + var source = string.Join(',', sourceFiles.Select(s => s.MakeAbsolute(_environment).FullPath)); + builder.AppendSwitch("--snapshot-source", source.Quote()); + + // Set the Output file. + outputFile = outputFile.MakeAbsolute(_environment); + builder.AppendSwitch("--snapshot-output", outputFile.FullPath.Quote()); + + // Set the Temporary directory. + if (settings.TemporaryDirectory != null) + { + settings.TemporaryDirectory = settings.TemporaryDirectory.MakeAbsolute(_environment); + builder.AppendSwitch("--temporary-directory", settings.TemporaryDirectory.FullPath.Quote()); + } + } + + private void BuildLegacyArguments(IEnumerable sourceFiles, FilePath outputFile, ProcessArgumentBuilder builder) + { // Set the Source files. var source = string.Join(';', sourceFiles.Select(s => s.MakeAbsolute(_environment).FullPath)); builder.AppendSwitch("/Source", "=", source.Quote()); @@ -75,11 +111,6 @@ private ProcessArgumentBuilder GetArguments( // Set the Output file. outputFile = outputFile.MakeAbsolute(_environment); builder.AppendSwitch("/Output", "=", outputFile.FullPath.Quote()); - - // Get Global settings - GetArguments(settings).CopyTo(builder); - - return builder; } } } From 21534db7253716986da82a6db0ade3277c525c4c Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Wed, 17 Jun 2026 07:56:04 +0200 Subject: [PATCH 05/20] feat: merge output param is optional for new param syntax --- .../DotCover/Merge/DotCoverMergerTests.cs | 38 +++++++++++++------ .../Tools/DotCover/Merge/DotCoverMerger.cs | 12 ++++-- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs index e0767b44c5..04b9a4a0a6 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs @@ -41,35 +41,36 @@ public void Should_Throw_If_Source_Files_Is_Empty() } [Fact] - public void Should_Throw_If_Output_File_Is_Null() + public void Should_Throw_If_Settings_Are_Null() { // Given var fixture = new DotCoverMergerFixture(); - fixture.OutputFile = null; + fixture.Settings = null; // When var result = Record.Exception(() => fixture.Run()); // Then - AssertEx.IsArgumentNullException(result, "outputFile"); + AssertEx.IsArgumentNullException(result, "settings"); } + #region New Parameter Syntax + [Fact] - public void Should_Throw_If_Settings_Are_Null() + public void Should_Ignore_Output_If_Not_Set() { // Given var fixture = new DotCoverMergerFixture(); - fixture.Settings = null; - + fixture.SourceFiles = new List { new ("/Working/result1.dcvr"), new ("/Working/result2.dcvr") }; + fixture.OutputFile = null; // When - var result = Record.Exception(() => fixture.Run()); + var result = fixture.Run(); // Then - AssertEx.IsArgumentNullException(result, "settings"); + Assert.Equal("merge " + + "--snapshot-source \"/Working/result1.dcvr,/Working/result2.dcvr\"", result.Args); } - #region New Parameter Syntax - [Fact] public void Should_Set_Correct_Arguments() { @@ -159,7 +160,7 @@ public void Should_Append_LogFile_LegacySyntax() } [Fact] - public void Should_Append_ConfigurationFile() + public void Should_Append_ConfigurationFile_LegacySyntax() { // Given var fixture = new DotCoverMergerFixture(); @@ -175,6 +176,21 @@ public void Should_Append_ConfigurationFile() "/Output=\"/Working/result.dcvr\"", result.Args); } + [Fact] + public void Should_Throw_If_Output_File_Is_Null_LegacySyntax() + { + // Given + var fixture = new DotCoverMergerFixture(); + fixture.OutputFile = null; + fixture.Settings.UseLegacySyntax = true; + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + AssertEx.IsArgumentNullException(result, "outputFile"); + } + #endregion } } diff --git a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs index b480e5fc5b..1a87f847ca 100644 --- a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs +++ b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs @@ -49,8 +49,11 @@ public void Merge( { throw new ArgumentNullException("sourceFiles"); } - ArgumentNullException.ThrowIfNull(outputFile); ArgumentNullException.ThrowIfNull(settings); + if (settings.UseLegacySyntax) + { + ArgumentNullException.ThrowIfNull(outputFile); + } // Run the tool. Run(settings, GetArguments(sourceFiles, outputFile, settings)); @@ -91,8 +94,11 @@ private void BuildNewArguments(IEnumerable sourceFiles, FilePath outpu builder.AppendSwitch("--snapshot-source", source.Quote()); // Set the Output file. - outputFile = outputFile.MakeAbsolute(_environment); - builder.AppendSwitch("--snapshot-output", outputFile.FullPath.Quote()); + if (outputFile != null) + { + outputFile = outputFile.MakeAbsolute(_environment); + builder.AppendSwitch("--snapshot-output", outputFile.FullPath.Quote()); + } // Set the Temporary directory. if (settings.TemporaryDirectory != null) From 09aef549ab6b84ef20bd8633820181427b6b0a33 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Wed, 17 Jun 2026 09:09:15 +0200 Subject: [PATCH 06/20] feat: Added possibility to merge without specified outputfile --- .../Tools/DotCover/Merge/DotCoverMerger.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs index 1a87f847ca..97c5654eaf 100644 --- a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs +++ b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs @@ -34,6 +34,17 @@ public DotCoverMerger( _environment = environment; } + /// + /// Runs DotCover Merge with the new parameter Syntax. + /// + /// The list of DotCover coverage snapshot files. + /// The settings. + /// The merged output file (optional). + public void Merge(IEnumerable sourceFiles, DotCoverMergeSettings settings, FilePath outputFile = null) + { + Merge(sourceFiles, outputFile, settings); + } + /// /// Runs DotCover Merge with the specified settings. /// From 8a4e1bcb5d5c67c3c707ae708db3ee2ae05be390 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Wed, 17 Jun 2026 12:49:45 +0200 Subject: [PATCH 07/20] feat: implemented new parameter syntax in DotCoverReporter --- .../DotCover/Report/DotCoverReporterTests.cs | 129 ++++++++++++++++-- .../Tools/DotCover/DotCoverAliases.cs | 39 ++++++ .../DotCover/Report/DotCoverReportSettings.cs | 28 +++- .../Tools/DotCover/Report/DotCoverReporter.cs | 81 +++++++++-- 4 files changed, 256 insertions(+), 21 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs index 468f0c17a5..0b85476a44 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs @@ -5,7 +5,6 @@ using Cake.Common.Tests.Fixtures.Tools.DotCover.Report; using Cake.Common.Tools.DotCover; using Cake.Core.IO; -using Xunit; namespace Cake.Common.Tests.Unit.Tools.DotCover.Report { @@ -28,31 +27,136 @@ public void Should_Throw_If_Source_File_Is_Null() } [Fact] - public void Should_Throw_If_Output_File_Is_Null() + public void Should_Throw_If_Settings_Are_Null() { // Given var fixture = new DotCoverReporterFixture(); - fixture.OutputFile = null; + fixture.Settings = null; // When var result = Record.Exception(() => fixture.Run()); // Then - AssertEx.IsArgumentNullException(result, "outputFile"); + AssertEx.IsArgumentNullException(result, "settings"); } + #region New Parameter Syntax + [Fact] - public void Should_Throw_If_Settings_Are_Null() + public void Should_Ignore_Output_File_If_Null() { // Given var fixture = new DotCoverReporterFixture(); - fixture.Settings = null; + fixture.OutputFile = null; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("report " + + "--snapshot-source \"/Working/result.dcvr\"", result.Args); + } + + [Fact] + public void Should_Append_JsonReportOutput() + { + // Given + var fixture = new DotCoverReporterFixture(); + fixture.OutputFile = null; + fixture.Settings.JsonReportOutput = new FilePath("/Working/coverage.json"); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("report " + + "--snapshot-source \"/Working/result.dcvr\" " + + "--json-report-output \"/Working/coverage.json\"", result.Args); + } + + [Theory] + [InlineData(DotCoverReportScope.None, "none")] + [InlineData(DotCoverReportScope.Assembly, "assembly")] + [InlineData(DotCoverReportScope.Type, "type")] + [InlineData(DotCoverReportScope.Method, "method")] + [InlineData(DotCoverReportScope.Statement, "statement")] + public void Should_Append_JsonReportScope(DotCoverReportScope reportScope, string reportScopeString) + { + // Given + var fixture = new DotCoverReporterFixture(); + fixture.OutputFile = null; + fixture.Settings.JsonReportOutput = new FilePath("/Working/coverage.json"); + fixture.Settings.JsonReportCoveringTestsScope = reportScope; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("report " + + "--snapshot-source \"/Working/result.dcvr\" " + + "--json-report-output \"/Working/coverage.json\" " + + "--json-report-covering-tests-scope \"" + reportScopeString + "\"", result.Args); + } + + + [Fact] + public void Should_Append_XmlReportOutput() + { + // Given + var fixture = new DotCoverReporterFixture(); + fixture.OutputFile = null; + fixture.Settings.XmlReportOutput = new FilePath("/Working/coverage.json"); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("report " + + "--snapshot-source \"/Working/result.dcvr\" " + + "--xml-report-output \"/Working/coverage.json\"", result.Args); + } + + [Theory] + [InlineData(DotCoverReportScope.None, "none")] + [InlineData(DotCoverReportScope.Assembly, "assembly")] + [InlineData(DotCoverReportScope.Type, "type")] + [InlineData(DotCoverReportScope.Method, "method")] + [InlineData(DotCoverReportScope.Statement, "statement")] + public void Should_Append_XmlReportScope(DotCoverReportScope reportScope, string reportScopeString) + { + // Given + var fixture = new DotCoverReporterFixture(); + fixture.OutputFile = null; + fixture.Settings.XmlReportOutput = new FilePath("/Working/coverage.json"); + fixture.Settings.XmlReportCoveringTestsScope = reportScope; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("report " + + "--snapshot-source \"/Working/result.dcvr\" " + + "--xml-report-output \"/Working/coverage.json\" " + + "--xml-report-covering-tests-scope \"" + reportScopeString + "\"", result.Args); + } + + #endregion + + #region Legacy Parameter Syntax + + [Fact] + public void Should_Throw_If_Output_File_Is_Null() + { + // Given + var fixture = new DotCoverReporterFixture(); + fixture.OutputFile = null; + fixture.Settings.UseLegacySyntax = true; // When var result = Record.Exception(() => fixture.Run()); // Then - AssertEx.IsArgumentNullException(result, "settings"); + AssertEx.IsArgumentNullException(result, "outputFile"); } [Theory] @@ -65,12 +169,13 @@ public void Should_Append_ReportType(DotCoverReportType reportType, string repor // Given var fixture = new DotCoverReporterFixture(); fixture.Settings.ReportType = reportType; + fixture.Settings.UseLegacySyntax = true; // When var result = fixture.Run(); // Then - Assert.Equal("Report " + + Assert.Equal("report " + "/Source=\"/Working/result.dcvr\" " + "/Output=\"/Working/result.xml\" " + "/ReportType=" + reportTypeString, result.Args); @@ -82,12 +187,13 @@ public void Should_Append_LogFile() // Given var fixture = new DotCoverReporterFixture(); fixture.Settings.LogFile = "./logfile.log"; + fixture.Settings.UseLegacySyntax = true; // When var result = fixture.Run(); // Then - Assert.Equal("Report " + + Assert.Equal("report " + "/Source=\"/Working/result.dcvr\" " + "/Output=\"/Working/result.xml\" " + "/LogFile=\"/Working/logfile.log\"", result.Args); @@ -99,15 +205,18 @@ public void Should_Append_ConfigurationFile() // Given var fixture = new DotCoverReporterFixture(); fixture.Settings.WithConfigFile(new FilePath("./config.xml")); + fixture.Settings.UseLegacySyntax = true; // When var result = fixture.Run(); // Then - Assert.Equal("Report \"/Working/config.xml\" " + + Assert.Equal("report \"/Working/config.xml\" " + "/Source=\"/Working/result.dcvr\" " + "/Output=\"/Working/result.xml\"", result.Args); } + + #endregion } } } \ No newline at end of file diff --git a/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs b/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs index cc0cab137f..be0e8796c9 100644 --- a/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs +++ b/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs @@ -163,6 +163,45 @@ public static void DotCoverReport( reporter.Report(sourceFile, outputFile, settings); } + /// + /// Runs DotCover Report + /// for the specified action and settings. + /// + /// The context. + /// The DotCover coverage snapshot file name. + /// The settings. + /// + /// + /// DotCoverReport(new FilePath("./result.dcvr"), + /// new DotCoverReportSettings { + /// ReportType = DotCoverReportType.HTML + /// }); + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Report")] + [CakeNamespaceImport("Cake.Common.Tools.DotCover.Report")] + public static void DotCoverReport( + this ICakeContext context, + FilePath sourceFile, + DotCoverReportSettings settings) + { + ArgumentNullException.ThrowIfNull(context); + + if (settings == null) + { + settings = new DotCoverReportSettings(); + } + + // Create the DotCover reporter. + var reporter = new DotCoverReporter( + context.FileSystem, context.Environment, + context.ProcessRunner, context.Tools); + + // Run DotCover report. + reporter.Report(sourceFile, settings); + } + /// /// Runs DotCover Merge /// for the specified action and settings. diff --git a/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs b/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs index 0e04d50b72..a44d6848a0 100644 --- a/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs +++ b/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Cake.Core.IO; + namespace Cake.Common.Tools.DotCover.Report { /// @@ -15,5 +17,29 @@ public sealed class DotCoverReportSettings : DotCoverSettings /// The Default value is . /// public DotCoverReportType ReportType { get; set; } + + /// + /// Gets the path to save a formatted JSON report. + /// This represents the --json-report-output option. + /// + public FilePath JsonReportOutput { get; set; } + + /// + /// Gets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] + /// This represents the --json-report-covering-tests-scope option. + /// + public DotCoverReportScope? JsonReportCoveringTestsScope { get; set; } + + /// + /// Gets the path to save a formatted JSON report. + /// This represents the --xml-report-output option. + /// + public FilePath XmlReportOutput { get; set; } + + /// + /// Gets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] + /// This represents the --xml-report-covering-tests-scope option. + /// + public DotCoverReportScope? XmlReportCoveringTestsScope { get; set; } } -} +} \ No newline at end of file diff --git a/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs b/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs index 870ed0a1a9..90eed927bf 100644 --- a/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs +++ b/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs @@ -44,25 +44,91 @@ public void Report( DotCoverReportSettings settings) { ArgumentNullException.ThrowIfNull(sourceFile); - ArgumentNullException.ThrowIfNull(outputFile); ArgumentNullException.ThrowIfNull(settings); + if (settings.UseLegacySyntax) + { + ArgumentNullException.ThrowIfNull(outputFile); + } + // Run the tool. - Run(settings, GetArguments(sourceFile, outputFile, settings)); + Run(settings, GetArguments(sourceFile, settings, outputFile)); } - private ProcessArgumentBuilder GetArguments( + /// + /// Runs DotCover Cover with the specified settings. + /// + /// The DotCover coverage snapshot file name. + /// The settings. + public void Report( FilePath sourceFile, - FilePath outputFile, DotCoverReportSettings settings) + { + ArgumentNullException.ThrowIfNull(sourceFile); + ArgumentNullException.ThrowIfNull(settings); + + // Run the tool. + Run(settings, GetArguments(sourceFile, settings)); + } + + private ProcessArgumentBuilder GetArguments( + FilePath sourceFile, + DotCoverReportSettings settings, FilePath outputFile = null) { var builder = new ProcessArgumentBuilder(); - builder.Append("Report"); + builder.Append("report"); // Set configuration file if exists. GetConfigurationFileArgument(settings).CopyTo(builder); + if (settings.UseLegacySyntax) + { + GenerateLegacyArguments(sourceFile, outputFile, settings, builder); + } + else + { + GenerateArguments(sourceFile, settings, builder); + } + + // Get Global settings + GetArguments(settings).CopyTo(builder); + + return builder; + } + + private void GenerateArguments(FilePath sourceFile, DotCoverReportSettings settings, ProcessArgumentBuilder builder) + { + // Set the Source file. + builder.AppendSwitch("--snapshot-source", sourceFile.MakeAbsolute(_environment).FullPath.Quote()); + + // Set Json report output + if (settings.JsonReportOutput != null) + { + builder.AppendSwitch("--json-report-output", settings.JsonReportOutput.MakeAbsolute(_environment).FullPath.Quote()); + } + + // Set test scope, ignore default value + if (settings.JsonReportCoveringTestsScope.HasValue) + { + builder.AppendSwitch("--json-report-covering-tests-scope", settings.JsonReportCoveringTestsScope.Value.ToString().ToLowerInvariant().Quote()); + } + + // Set Xml report output + if (settings.XmlReportOutput != null) + { + builder.AppendSwitch("--xml-report-output", settings.XmlReportOutput.MakeAbsolute(_environment).FullPath.Quote()); + } + + // Set test scope, ignore default value + if (settings.XmlReportCoveringTestsScope.HasValue) + { + builder.AppendSwitch("--xml-report-covering-tests-scope", settings.XmlReportCoveringTestsScope.Value.ToString().ToLowerInvariant().Quote()); + } + } + + private void GenerateLegacyArguments(FilePath sourceFile, FilePath outputFile, DotCoverReportSettings settings, ProcessArgumentBuilder builder) + { // Set the Source file. sourceFile = sourceFile.MakeAbsolute(_environment); builder.AppendSwitch("/Source", "=", sourceFile.FullPath.Quote()); @@ -76,11 +142,6 @@ private ProcessArgumentBuilder GetArguments( { builder.AppendSwitch("/ReportType", "=", settings.ReportType.ToString()); } - - // Get Global settings - GetArguments(settings).CopyTo(builder); - - return builder; } } } From 381c83569030736583b5c74a9131d89c9e2f0b67 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Wed, 17 Jun 2026 16:10:06 +0200 Subject: [PATCH 08/20] feat: fix lint errors --- .../Unit/Tools/DotCover/Analyse/DotCoverAnalyserTests.cs | 1 + .../Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs | 1 + .../Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs | 3 ++- .../Unit/Tools/DotCover/Report/DotCoverReporterTests.cs | 2 +- .../Tools/DotCover/Report/DotCoverReportSettings.cs | 8 ++++---- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Analyse/DotCoverAnalyserTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Analyse/DotCoverAnalyserTests.cs index d3a39025ac..b2b5e3f010 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Analyse/DotCoverAnalyserTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Analyse/DotCoverAnalyserTests.cs @@ -241,6 +241,7 @@ public void Should_Append_LogFile() // Given var fixture = new DotCoverAnalyserFixture(); fixture.Settings.LogFile = "./logfile.log"; + fixture.Settings.UseLegacySyntax = true; // When var result = fixture.Run(); diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs index 5f76af5c60..9363875893 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs @@ -9,6 +9,7 @@ using Cake.Common.Tools.XUnit; using Cake.Core.IO; using Cake.Testing; +using Xunit; namespace Cake.Common.Tests.Unit.Tools.DotCover.Cover { diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs index 04b9a4a0a6..2f3f4d7040 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs @@ -2,10 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using Cake.Common.Tests.Fixtures.Tools.DotCover.Merge; using Cake.Common.Tools.DotCover; using Cake.Core.IO; - +using Xunit; namespace Cake.Common.Tests.Unit.Tools.DotCover.Merge { public sealed class DotCoverMergerTests diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs index 0b85476a44..3980f0d83b 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs @@ -5,6 +5,7 @@ using Cake.Common.Tests.Fixtures.Tools.DotCover.Report; using Cake.Common.Tools.DotCover; using Cake.Core.IO; +using Xunit; namespace Cake.Common.Tests.Unit.Tools.DotCover.Report { @@ -98,7 +99,6 @@ public void Should_Append_JsonReportScope(DotCoverReportScope reportScope, strin "--json-report-covering-tests-scope \"" + reportScopeString + "\"", result.Args); } - [Fact] public void Should_Append_XmlReportOutput() { diff --git a/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs b/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs index a44d6848a0..a1a98382c5 100644 --- a/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs +++ b/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs @@ -19,25 +19,25 @@ public sealed class DotCoverReportSettings : DotCoverSettings public DotCoverReportType ReportType { get; set; } /// - /// Gets the path to save a formatted JSON report. + /// Gets or sets the path to save a formatted JSON report. /// This represents the --json-report-output option. /// public FilePath JsonReportOutput { get; set; } /// - /// Gets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] + /// Gets or sets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] /// This represents the --json-report-covering-tests-scope option. /// public DotCoverReportScope? JsonReportCoveringTestsScope { get; set; } /// - /// Gets the path to save a formatted JSON report. + /// Gets or sets the path to save a formatted JSON report. /// This represents the --xml-report-output option. /// public FilePath XmlReportOutput { get; set; } /// - /// Gets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] + /// Gets or sets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] /// This represents the --xml-report-covering-tests-scope option. /// public DotCoverReportScope? XmlReportCoveringTestsScope { get; set; } From b4d435e79d76e72ad6828bc88976f5d36f5521e9 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Tue, 16 Jun 2026 17:36:11 +0200 Subject: [PATCH 09/20] feat: added tests for legacy parameter syntax --- .../DotCover/Cover/DotCoverCovererTests.cs | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs index f98ab9b6f9..229deb442b 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs @@ -87,6 +87,8 @@ public void Should_Throw_If_No_Tool_Was_Intercepted() AssertEx.IsCakeException(result, "No tool was started."); } + #region New Paramter Syntax + [Fact] public void Should_Capture_Tool_And_Arguments_From_Action() { @@ -534,6 +536,228 @@ public void Should_Support_New_Features_In_New_Mode() Assert.Contains("--exclude-assemblies \"*.Tests\"", result.Args); Assert.Contains("--snapshot-output", result.Args); } + + #endregion + + #region Legacy Paramter Syntax + + [Fact] + public void Should_Capture_Tool_And_Arguments_From_Action_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\"", result.Args); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + public void Should_Not_Capture_Arguments_From_Action_If_Excluded_LegacySyntax(string arguments) + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Action = context => + { + context.ProcessRunner.Start( + new FilePath("/Working/tools/Test.exe"), + new ProcessSettings() + { + Arguments = arguments + }); + }; + fixture.Settings.WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/Output=\"/Working/result.dcvr\"", result.Args); + } + + [Fact] + public void Should_Append_TargetWorkingDir_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.TargetWorkingDir = new DirectoryPath("/Working"); + fixture.Settings.WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\" " + + "/TargetWorkingDir=\"/Working\"", result.Args); + } + + [Fact] + public void Should_Append_Scope_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.WithScope("/Working/*.dll") + .WithScope("/Some/**/Other/*.dll") + .WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\" " + + "/Scope=\"/Working/*.dll;/Some/**/Other/*.dll\"", result.Args); + } + + [Fact] + public void Should_Append_Filters_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.WithFilter("+:module=Test.*") + .WithFilter("-:myassembly") + .WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\" " + + "/Filters=\"+:module=Test.*;-:myassembly\"", result.Args); + } + + [Fact] + public void Should_Append_AttributeFilters_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.WithAttributeFilter("filter1") + .WithAttributeFilter("filter2") + .WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\" " + + "/AttributeFilters=\"filter1;filter2\"", result.Args); + } + + [Fact] + public void Should_Append_DisableDefaultFilters_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.DisableDefaultFilters = true; + fixture.Settings.WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\" " + + "/DisableDefaultFilters", result.Args); + } + + [Fact] + public void Should_Append_ProcessFilters_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.WithProcessFilter("+:test.exe") + .WithProcessFilter("-:sqlservr.exe") + .WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\" " + + "/ProcessFilters=\"+:test.exe;-:sqlservr.exe\"", result.Args); + } + + [Fact] + public void Should_Capture_XUnit_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.FileSystem.CreateFile("/Working/tools/xunit.console.exe"); + fixture.Action = context => + { + context.XUnit2( + new FilePath[] { "./Test.dll" }, + new XUnit2Settings { ShadowCopy = false }); + }; + fixture.Settings.WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/xunit.console.exe\" " + + "/TargetArguments=\"\\\"/Working/Test.dll\\\" -noshadow\" " + + "/Output=\"/Working/result.dcvr\"", result.Args); + } + + [Fact] + public void Should_Capture_NUnit_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.FileSystem.CreateFile("/Working/tools/nunit-console.exe"); + fixture.Action = context => + { + context.NUnit( + new FilePath[] { "./Test.dll" }, + new NUnitSettings { ShadowCopy = false }); + }; + fixture.Settings.WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover /TargetExecutable=\"/Working/tools/nunit-console.exe\" " + + "/TargetArguments=\"\\\"/Working/Test.dll\\\" -noshadow\" " + + "/Output=\"/Working/result.dcvr\"", result.Args); + } + + [Fact] + public void Should_Append_ConfigurationFile_LegacySyntax() + { + // Given + var fixture = new DotCoverCovererFixture(); + fixture.Settings.WithConfigFile(new FilePath("./config.xml")).WithLegacySyntax(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("cover \"/Working/config.xml\" /TargetExecutable=\"/Working/tools/Test.exe\" " + + "/TargetArguments=\"-argument\" " + + "/Output=\"/Working/result.dcvr\"", result.Args); + } + + #endregion } } } \ No newline at end of file From 36c068c388e236b71b5c0dcf3439347108f015ee Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Tue, 16 Jun 2026 17:37:02 +0200 Subject: [PATCH 10/20] feat: moved UseLegacySyntax property to DotCoverCoverSettings --- .../Tools/DotCover/Cover/DotCoverCoverSettings.cs | 8 -------- src/Cake.Common/Tools/DotCover/DotCoverSettings.cs | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverSettings.cs b/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverSettings.cs index eadc7ce291..66d8d884e6 100644 --- a/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverSettings.cs +++ b/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverSettings.cs @@ -52,13 +52,5 @@ public sealed class DotCoverCoverSettings : DotCoverCoverageSettings /// This represents the --no-ngen option. /// public bool NoNGen { get; set; } - - /// - /// Gets or sets a value indicating whether to use the legacy command syntax. - /// When true, uses old format like '/TargetExecutable="/path"'. - /// When false, uses new format like '--target-executable "/path"'. - /// Default is false (new format). - /// - public bool UseLegacySyntax { get; set; } } } \ No newline at end of file diff --git a/src/Cake.Common/Tools/DotCover/DotCoverSettings.cs b/src/Cake.Common/Tools/DotCover/DotCoverSettings.cs index c1e67be183..e5edf53d91 100644 --- a/src/Cake.Common/Tools/DotCover/DotCoverSettings.cs +++ b/src/Cake.Common/Tools/DotCover/DotCoverSettings.cs @@ -24,5 +24,13 @@ public abstract class DotCoverSettings : ToolSettings /// to specifying all parameters in-line or having them in a batch file. /// public FilePath ConfigFile { get; set; } + + /// + /// Gets or sets a value indicating whether to use the legacy command syntax. + /// When true, uses old format like '/TargetExecutable="/path"'. + /// When false, uses new format like '--target-executable "/path"'. + /// Default is false (new format). + /// + public bool UseLegacySyntax { get; set; } } } From f8bf9e23e8bf27b23db96f445ec18b0fb90c3afa Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Tue, 16 Jun 2026 17:48:21 +0200 Subject: [PATCH 11/20] fix: removed legacy parameters from new GetCoverCoverageArguments fnc --- .../DotCover/Cover/DotCoverCovererTests.cs | 46 ++++++++----------- .../Tools/DotCover/Cover/DotCoverCoverer.cs | 35 -------------- 2 files changed, 20 insertions(+), 61 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs index 229deb442b..5f76af5c60 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs @@ -9,7 +9,6 @@ using Cake.Common.Tools.XUnit; using Cake.Core.IO; using Cake.Testing; -using Xunit; namespace Cake.Common.Tests.Unit.Tools.DotCover.Cover { @@ -147,12 +146,12 @@ public void Should_Append_TargetWorkingDir() } [Fact] - public void Should_Append_Scope() + public void Should_Not_Append_Scope() { // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithScope("/Working/*.dll") - .WithScope("/Some/**/Other/*.dll"); + .WithScope("/Some/**/Other/*.dll"); // When var result = fixture.Run(); @@ -160,17 +159,16 @@ public void Should_Append_Scope() // Then Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + - "--snapshot-output \"/Working/result.dcvr\" " + - "/Scope=\"/Working/*.dll;/Some/**/Other/*.dll\"", result.Args); + "--snapshot-output \"/Working/result.dcvr\"", result.Args); } [Fact] - public void Should_Append_Filters() + public void Should_Not_Append_Filters() { // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithFilter("+:module=Test.*") - .WithFilter("-:myassembly"); + .WithFilter("-:myassembly"); // When var result = fixture.Run(); @@ -178,17 +176,16 @@ public void Should_Append_Filters() // Then Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + - "--snapshot-output \"/Working/result.dcvr\" " + - "/Filters=\"+:module=Test.*;-:myassembly\"", result.Args); + "--snapshot-output \"/Working/result.dcvr\"", result.Args); } [Fact] - public void Should_Append_AttributeFilters() + public void Should_Not_Append_AttributeFilters() { // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithAttributeFilter("filter1") - .WithAttributeFilter("filter2"); + .WithAttributeFilter("filter2"); // When var result = fixture.Run(); @@ -196,12 +193,11 @@ public void Should_Append_AttributeFilters() // Then Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + - "--snapshot-output \"/Working/result.dcvr\" " + - "/AttributeFilters=\"filter1;filter2\"", result.Args); + "--snapshot-output \"/Working/result.dcvr\"", result.Args); } [Fact] - public void Should_Append_DisableDefaultFilters() + public void Should_Not_Append_DisableDefaultFilters() { // Given var fixture = new DotCoverCovererFixture(); @@ -213,17 +209,16 @@ public void Should_Append_DisableDefaultFilters() // Then Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + - "--snapshot-output \"/Working/result.dcvr\" " + - "/DisableDefaultFilters", result.Args); + "--snapshot-output \"/Working/result.dcvr\"", result.Args); } [Fact] - public void Should_Append_ProcessFilters() + public void Should_Not_Append_ProcessFilters() { // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithProcessFilter("+:test.exe") - .WithProcessFilter("-:sqlservr.exe"); + .WithProcessFilter("-:sqlservr.exe"); // When var result = fixture.Run(); @@ -231,8 +226,7 @@ public void Should_Append_ProcessFilters() // Then Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + - "--snapshot-output \"/Working/result.dcvr\" " + - "/ProcessFilters=\"+:test.exe;-:sqlservr.exe\"", result.Args); + "--snapshot-output \"/Working/result.dcvr\"", result.Args); } [Fact] @@ -301,7 +295,7 @@ public void Should_Append_ExcludeAssemblies() // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithExcludeAssembly("*.Tests") - .WithExcludeAssembly("Test.*"); + .WithExcludeAssembly("Test.*"); // When var result = fixture.Run(); @@ -319,7 +313,7 @@ public void Should_Append_ExcludeAttributes() // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithExcludeAttribute("System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute") - .WithExcludeAttribute("Custom.*Attribute"); + .WithExcludeAttribute("Custom.*Attribute"); // When var result = fixture.Run(); @@ -337,7 +331,7 @@ public void Should_Append_ExcludeProcesses() // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithExcludeProcess("test.exe") - .WithExcludeProcess("*.vshost.exe"); + .WithExcludeProcess("*.vshost.exe"); // When var result = fixture.Run(); @@ -506,8 +500,8 @@ public void Should_Not_Support_New_Features_In_Legacy_Mode() // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithLegacySyntax() - .WithJsonReportOutput(new FilePath("/Working/report.json")) - .WithExcludeAssembly("*.Tests"); + .WithJsonReportOutput(new FilePath("/Working/report.json")) + .WithExcludeAssembly("*.Tests"); // When var result = fixture.Run(); @@ -526,7 +520,7 @@ public void Should_Support_New_Features_In_New_Mode() // Given var fixture = new DotCoverCovererFixture(); fixture.Settings.WithJsonReportOutput(new FilePath("/Working/report.json")) - .WithExcludeAssembly("*.Tests"); + .WithExcludeAssembly("*.Tests"); // When var result = fixture.Run(); diff --git a/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs b/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs index c3d53ab657..96e5339908 100644 --- a/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs +++ b/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs @@ -165,41 +165,6 @@ private ProcessArgumentBuilder GetCoverCoverageArguments(DotCoverCoverageSetting builder.AppendSwitch("--exclude-processes", excludeProcesses.Quote()); } - // Legacy filtering options (maintain backward compatibility with old format) - // Scope - if (settings.Scope.Count > 0) - { - var scope = string.Join(';', settings.Scope); - builder.AppendSwitch("/Scope", "=", scope.Quote()); - } - - // Filters - if (settings.Filters.Count > 0) - { - var filters = string.Join(';', settings.Filters); - builder.AppendSwitch("/Filters", "=", filters.Quote()); - } - - // AttributeFilters - if (settings.AttributeFilters.Count > 0) - { - var attributeFilters = string.Join(';', settings.AttributeFilters); - builder.AppendSwitch("/AttributeFilters", "=", attributeFilters.Quote()); - } - - // ProcessFilters - if (settings.ProcessFilters.Count > 0) - { - var processFilters = string.Join(';', settings.ProcessFilters); - builder.AppendSwitch("/ProcessFilters", "=", processFilters.Quote()); - } - - // DisableDefaultFilters - if (settings.DisableDefaultFilters) - { - builder.Append("/DisableDefaultFilters"); - } - return builder; } From f3f3d5ed975757ae82c138153618a9f8f46a472f Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Wed, 17 Jun 2026 07:50:09 +0200 Subject: [PATCH 12/20] feat: implemented new parameter syntax in DotCoverMerger --- .../DotCover/Merge/DotCoverMergerTests.cs | 82 ++++++++++++++++++- .../Tools/DotCover/DotCoverTool.cs | 12 ++- .../DotCover/Merge/DotCoverMergeSettings.cs | 7 ++ .../Tools/DotCover/Merge/DotCoverMerger.cs | 43 ++++++++-- 4 files changed, 132 insertions(+), 12 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs index 238584413e..e0767b44c5 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs @@ -2,11 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using Cake.Common.Tests.Fixtures.Tools.DotCover.Merge; using Cake.Common.Tools.DotCover; using Cake.Core.IO; -using Xunit; namespace Cake.Common.Tests.Unit.Tools.DotCover.Merge { @@ -70,6 +68,57 @@ public void Should_Throw_If_Settings_Are_Null() AssertEx.IsArgumentNullException(result, "settings"); } + #region New Parameter Syntax + + [Fact] + public void Should_Set_Correct_Arguments() + { + // Given + var fixture = new DotCoverMergerFixture(); + fixture.SourceFiles = new List { new ("/Working/result1.dcvr"), new ("/Working/result2.dcvr") }; + fixture.OutputFile = new FilePath("/Working/output.dcvr"); + // When + var result = fixture.Run(); + + // Then + Assert.Equal("merge " + + "--snapshot-source \"/Working/result1.dcvr,/Working/result2.dcvr\" " + + "--snapshot-output \"/Working/output.dcvr\"", result.Args); + } + + [Fact] + public void Should_Append_TemporaryDirectory() + { + // Given + var fixture = new DotCoverMergerFixture(); + fixture.Settings.TemporaryDirectory = new DirectoryPath("/Working/temp"); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("merge " + + "--snapshot-source \"/Working/result1.dcvr,/Working/result2.dcvr\" " + + "--snapshot-output \"/Working/result.dcvr\" " + + "--temporary-directory \"/Working/temp\"", result.Args); + } + + [Fact] + public void Should_Not_Append_Null_TemporaryDirectory() + { + // Given + var fixture = new DotCoverMergerFixture(); + fixture.Settings.TemporaryDirectory = null; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("merge " + + "--snapshot-source \"/Working/result1.dcvr,/Working/result2.dcvr\" " + + "--snapshot-output \"/Working/result.dcvr\"", result.Args); + } + [Fact] public void Should_Append_LogFile() { @@ -81,7 +130,29 @@ public void Should_Append_LogFile() var result = fixture.Run(); // Then - Assert.Equal("Merge " + + Assert.Equal("merge " + + "--snapshot-source \"/Working/result1.dcvr,/Working/result2.dcvr\" " + + "--snapshot-output \"/Working/result.dcvr\" " + + "--log-file \"/Working/logfile.log\"", result.Args); + } + + #endregion + + #region Legacy Parameter Syntax + + [Fact] + public void Should_Append_LogFile_LegacySyntax() + { + // Given + var fixture = new DotCoverMergerFixture(); + fixture.Settings.LogFile = "./logfile.log"; + fixture.Settings.UseLegacySyntax = true; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("merge " + "/Source=\"/Working/result1.dcvr;/Working/result2.dcvr\" " + "/Output=\"/Working/result.dcvr\" " + "/LogFile=\"/Working/logfile.log\"", result.Args); @@ -93,15 +164,18 @@ public void Should_Append_ConfigurationFile() // Given var fixture = new DotCoverMergerFixture(); fixture.Settings.WithConfigFile(new FilePath("./config.xml")); + fixture.Settings.UseLegacySyntax = true; // When var result = fixture.Run(); // Then - Assert.Equal("Merge \"/Working/config.xml\" " + + Assert.Equal("merge \"/Working/config.xml\" " + "/Source=\"/Working/result1.dcvr;/Working/result2.dcvr\" " + "/Output=\"/Working/result.dcvr\"", result.Args); } + + #endregion } } } \ No newline at end of file diff --git a/src/Cake.Common/Tools/DotCover/DotCoverTool.cs b/src/Cake.Common/Tools/DotCover/DotCoverTool.cs index 1293f35111..08ec603199 100644 --- a/src/Cake.Common/Tools/DotCover/DotCoverTool.cs +++ b/src/Cake.Common/Tools/DotCover/DotCoverTool.cs @@ -64,8 +64,16 @@ protected ProcessArgumentBuilder GetArguments(DotCoverSettings settings) // LogFile if (settings.LogFile != null) { - var logFilePath = settings.LogFile.MakeAbsolute(_environment); - builder.AppendSwitch("/LogFile", "=", logFilePath.FullPath.Quote()); + if (settings.UseLegacySyntax) + { + var logFilePath = settings.LogFile.MakeAbsolute(_environment); + builder.AppendSwitch("/LogFile", "=", logFilePath.FullPath.Quote()); + } + else + { + var logFilePath = settings.LogFile.MakeAbsolute(_environment); + builder.AppendSwitch("--log-file", logFilePath.FullPath.Quote()); + } } return builder; diff --git a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMergeSettings.cs b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMergeSettings.cs index 090de38cfa..ad95f89609 100644 --- a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMergeSettings.cs +++ b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMergeSettings.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Cake.Core.IO; + namespace Cake.Common.Tools.DotCover.Merge { /// @@ -9,5 +11,10 @@ namespace Cake.Common.Tools.DotCover.Merge /// public sealed class DotCoverMergeSettings : DotCoverSettings { + /// + /// Gets or sets the directory for temporary files. + /// This represents the --temporary-directory option. + /// + public DirectoryPath TemporaryDirectory { get; set; } } } diff --git a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs index 36f05ede9e..b480e5fc5b 100644 --- a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs +++ b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs @@ -63,11 +63,47 @@ private ProcessArgumentBuilder GetArguments( { var builder = new ProcessArgumentBuilder(); - builder.Append("Merge"); + // Command name - always lowercase 'merge' for both formats + builder.Append("merge"); // Set configuration file if exists. GetConfigurationFileArgument(settings).CopyTo(builder); + if (settings.UseLegacySyntax) + { + BuildLegacyArguments(sourceFiles, outputFile, builder); + } + else + { + BuildNewArguments(sourceFiles, outputFile, builder, settings); + } + + // Get Global settings + GetArguments(settings).CopyTo(builder); + + return builder; + } + + private void BuildNewArguments(IEnumerable sourceFiles, FilePath outputFile, ProcessArgumentBuilder builder, DotCoverMergeSettings settings) + { + // Set the Source files. + var source = string.Join(',', sourceFiles.Select(s => s.MakeAbsolute(_environment).FullPath)); + builder.AppendSwitch("--snapshot-source", source.Quote()); + + // Set the Output file. + outputFile = outputFile.MakeAbsolute(_environment); + builder.AppendSwitch("--snapshot-output", outputFile.FullPath.Quote()); + + // Set the Temporary directory. + if (settings.TemporaryDirectory != null) + { + settings.TemporaryDirectory = settings.TemporaryDirectory.MakeAbsolute(_environment); + builder.AppendSwitch("--temporary-directory", settings.TemporaryDirectory.FullPath.Quote()); + } + } + + private void BuildLegacyArguments(IEnumerable sourceFiles, FilePath outputFile, ProcessArgumentBuilder builder) + { // Set the Source files. var source = string.Join(';', sourceFiles.Select(s => s.MakeAbsolute(_environment).FullPath)); builder.AppendSwitch("/Source", "=", source.Quote()); @@ -75,11 +111,6 @@ private ProcessArgumentBuilder GetArguments( // Set the Output file. outputFile = outputFile.MakeAbsolute(_environment); builder.AppendSwitch("/Output", "=", outputFile.FullPath.Quote()); - - // Get Global settings - GetArguments(settings).CopyTo(builder); - - return builder; } } } From 286d468e91c67efdbea46b056b9e57186d750b54 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Wed, 17 Jun 2026 07:56:04 +0200 Subject: [PATCH 13/20] feat: merge output param is optional for new param syntax --- .../DotCover/Merge/DotCoverMergerTests.cs | 38 +++++++++++++------ .../Tools/DotCover/Merge/DotCoverMerger.cs | 12 ++++-- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs index e0767b44c5..04b9a4a0a6 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs @@ -41,35 +41,36 @@ public void Should_Throw_If_Source_Files_Is_Empty() } [Fact] - public void Should_Throw_If_Output_File_Is_Null() + public void Should_Throw_If_Settings_Are_Null() { // Given var fixture = new DotCoverMergerFixture(); - fixture.OutputFile = null; + fixture.Settings = null; // When var result = Record.Exception(() => fixture.Run()); // Then - AssertEx.IsArgumentNullException(result, "outputFile"); + AssertEx.IsArgumentNullException(result, "settings"); } + #region New Parameter Syntax + [Fact] - public void Should_Throw_If_Settings_Are_Null() + public void Should_Ignore_Output_If_Not_Set() { // Given var fixture = new DotCoverMergerFixture(); - fixture.Settings = null; - + fixture.SourceFiles = new List { new ("/Working/result1.dcvr"), new ("/Working/result2.dcvr") }; + fixture.OutputFile = null; // When - var result = Record.Exception(() => fixture.Run()); + var result = fixture.Run(); // Then - AssertEx.IsArgumentNullException(result, "settings"); + Assert.Equal("merge " + + "--snapshot-source \"/Working/result1.dcvr,/Working/result2.dcvr\"", result.Args); } - #region New Parameter Syntax - [Fact] public void Should_Set_Correct_Arguments() { @@ -159,7 +160,7 @@ public void Should_Append_LogFile_LegacySyntax() } [Fact] - public void Should_Append_ConfigurationFile() + public void Should_Append_ConfigurationFile_LegacySyntax() { // Given var fixture = new DotCoverMergerFixture(); @@ -175,6 +176,21 @@ public void Should_Append_ConfigurationFile() "/Output=\"/Working/result.dcvr\"", result.Args); } + [Fact] + public void Should_Throw_If_Output_File_Is_Null_LegacySyntax() + { + // Given + var fixture = new DotCoverMergerFixture(); + fixture.OutputFile = null; + fixture.Settings.UseLegacySyntax = true; + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + AssertEx.IsArgumentNullException(result, "outputFile"); + } + #endregion } } diff --git a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs index b480e5fc5b..1a87f847ca 100644 --- a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs +++ b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs @@ -49,8 +49,11 @@ public void Merge( { throw new ArgumentNullException("sourceFiles"); } - ArgumentNullException.ThrowIfNull(outputFile); ArgumentNullException.ThrowIfNull(settings); + if (settings.UseLegacySyntax) + { + ArgumentNullException.ThrowIfNull(outputFile); + } // Run the tool. Run(settings, GetArguments(sourceFiles, outputFile, settings)); @@ -91,8 +94,11 @@ private void BuildNewArguments(IEnumerable sourceFiles, FilePath outpu builder.AppendSwitch("--snapshot-source", source.Quote()); // Set the Output file. - outputFile = outputFile.MakeAbsolute(_environment); - builder.AppendSwitch("--snapshot-output", outputFile.FullPath.Quote()); + if (outputFile != null) + { + outputFile = outputFile.MakeAbsolute(_environment); + builder.AppendSwitch("--snapshot-output", outputFile.FullPath.Quote()); + } // Set the Temporary directory. if (settings.TemporaryDirectory != null) From 83218d583796e1898789587c597f530f0dbf655f Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Wed, 17 Jun 2026 09:09:15 +0200 Subject: [PATCH 14/20] feat: Added possibility to merge without specified outputfile --- .../Tools/DotCover/Merge/DotCoverMerger.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs index 1a87f847ca..97c5654eaf 100644 --- a/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs +++ b/src/Cake.Common/Tools/DotCover/Merge/DotCoverMerger.cs @@ -34,6 +34,17 @@ public DotCoverMerger( _environment = environment; } + /// + /// Runs DotCover Merge with the new parameter Syntax. + /// + /// The list of DotCover coverage snapshot files. + /// The settings. + /// The merged output file (optional). + public void Merge(IEnumerable sourceFiles, DotCoverMergeSettings settings, FilePath outputFile = null) + { + Merge(sourceFiles, outputFile, settings); + } + /// /// Runs DotCover Merge with the specified settings. /// From 12ba34810b3f6836a5bbbd497507bf6b03872449 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Wed, 17 Jun 2026 12:49:45 +0200 Subject: [PATCH 15/20] feat: implemented new parameter syntax in DotCoverReporter --- .../DotCover/Report/DotCoverReporterTests.cs | 129 ++++++++++++++++-- .../Tools/DotCover/DotCoverAliases.cs | 39 ++++++ .../DotCover/Report/DotCoverReportSettings.cs | 28 +++- .../Tools/DotCover/Report/DotCoverReporter.cs | 81 +++++++++-- 4 files changed, 256 insertions(+), 21 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs index 468f0c17a5..0b85476a44 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs @@ -5,7 +5,6 @@ using Cake.Common.Tests.Fixtures.Tools.DotCover.Report; using Cake.Common.Tools.DotCover; using Cake.Core.IO; -using Xunit; namespace Cake.Common.Tests.Unit.Tools.DotCover.Report { @@ -28,31 +27,136 @@ public void Should_Throw_If_Source_File_Is_Null() } [Fact] - public void Should_Throw_If_Output_File_Is_Null() + public void Should_Throw_If_Settings_Are_Null() { // Given var fixture = new DotCoverReporterFixture(); - fixture.OutputFile = null; + fixture.Settings = null; // When var result = Record.Exception(() => fixture.Run()); // Then - AssertEx.IsArgumentNullException(result, "outputFile"); + AssertEx.IsArgumentNullException(result, "settings"); } + #region New Parameter Syntax + [Fact] - public void Should_Throw_If_Settings_Are_Null() + public void Should_Ignore_Output_File_If_Null() { // Given var fixture = new DotCoverReporterFixture(); - fixture.Settings = null; + fixture.OutputFile = null; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("report " + + "--snapshot-source \"/Working/result.dcvr\"", result.Args); + } + + [Fact] + public void Should_Append_JsonReportOutput() + { + // Given + var fixture = new DotCoverReporterFixture(); + fixture.OutputFile = null; + fixture.Settings.JsonReportOutput = new FilePath("/Working/coverage.json"); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("report " + + "--snapshot-source \"/Working/result.dcvr\" " + + "--json-report-output \"/Working/coverage.json\"", result.Args); + } + + [Theory] + [InlineData(DotCoverReportScope.None, "none")] + [InlineData(DotCoverReportScope.Assembly, "assembly")] + [InlineData(DotCoverReportScope.Type, "type")] + [InlineData(DotCoverReportScope.Method, "method")] + [InlineData(DotCoverReportScope.Statement, "statement")] + public void Should_Append_JsonReportScope(DotCoverReportScope reportScope, string reportScopeString) + { + // Given + var fixture = new DotCoverReporterFixture(); + fixture.OutputFile = null; + fixture.Settings.JsonReportOutput = new FilePath("/Working/coverage.json"); + fixture.Settings.JsonReportCoveringTestsScope = reportScope; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("report " + + "--snapshot-source \"/Working/result.dcvr\" " + + "--json-report-output \"/Working/coverage.json\" " + + "--json-report-covering-tests-scope \"" + reportScopeString + "\"", result.Args); + } + + + [Fact] + public void Should_Append_XmlReportOutput() + { + // Given + var fixture = new DotCoverReporterFixture(); + fixture.OutputFile = null; + fixture.Settings.XmlReportOutput = new FilePath("/Working/coverage.json"); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("report " + + "--snapshot-source \"/Working/result.dcvr\" " + + "--xml-report-output \"/Working/coverage.json\"", result.Args); + } + + [Theory] + [InlineData(DotCoverReportScope.None, "none")] + [InlineData(DotCoverReportScope.Assembly, "assembly")] + [InlineData(DotCoverReportScope.Type, "type")] + [InlineData(DotCoverReportScope.Method, "method")] + [InlineData(DotCoverReportScope.Statement, "statement")] + public void Should_Append_XmlReportScope(DotCoverReportScope reportScope, string reportScopeString) + { + // Given + var fixture = new DotCoverReporterFixture(); + fixture.OutputFile = null; + fixture.Settings.XmlReportOutput = new FilePath("/Working/coverage.json"); + fixture.Settings.XmlReportCoveringTestsScope = reportScope; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("report " + + "--snapshot-source \"/Working/result.dcvr\" " + + "--xml-report-output \"/Working/coverage.json\" " + + "--xml-report-covering-tests-scope \"" + reportScopeString + "\"", result.Args); + } + + #endregion + + #region Legacy Parameter Syntax + + [Fact] + public void Should_Throw_If_Output_File_Is_Null() + { + // Given + var fixture = new DotCoverReporterFixture(); + fixture.OutputFile = null; + fixture.Settings.UseLegacySyntax = true; // When var result = Record.Exception(() => fixture.Run()); // Then - AssertEx.IsArgumentNullException(result, "settings"); + AssertEx.IsArgumentNullException(result, "outputFile"); } [Theory] @@ -65,12 +169,13 @@ public void Should_Append_ReportType(DotCoverReportType reportType, string repor // Given var fixture = new DotCoverReporterFixture(); fixture.Settings.ReportType = reportType; + fixture.Settings.UseLegacySyntax = true; // When var result = fixture.Run(); // Then - Assert.Equal("Report " + + Assert.Equal("report " + "/Source=\"/Working/result.dcvr\" " + "/Output=\"/Working/result.xml\" " + "/ReportType=" + reportTypeString, result.Args); @@ -82,12 +187,13 @@ public void Should_Append_LogFile() // Given var fixture = new DotCoverReporterFixture(); fixture.Settings.LogFile = "./logfile.log"; + fixture.Settings.UseLegacySyntax = true; // When var result = fixture.Run(); // Then - Assert.Equal("Report " + + Assert.Equal("report " + "/Source=\"/Working/result.dcvr\" " + "/Output=\"/Working/result.xml\" " + "/LogFile=\"/Working/logfile.log\"", result.Args); @@ -99,15 +205,18 @@ public void Should_Append_ConfigurationFile() // Given var fixture = new DotCoverReporterFixture(); fixture.Settings.WithConfigFile(new FilePath("./config.xml")); + fixture.Settings.UseLegacySyntax = true; // When var result = fixture.Run(); // Then - Assert.Equal("Report \"/Working/config.xml\" " + + Assert.Equal("report \"/Working/config.xml\" " + "/Source=\"/Working/result.dcvr\" " + "/Output=\"/Working/result.xml\"", result.Args); } + + #endregion } } } \ No newline at end of file diff --git a/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs b/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs index cc0cab137f..be0e8796c9 100644 --- a/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs +++ b/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs @@ -163,6 +163,45 @@ public static void DotCoverReport( reporter.Report(sourceFile, outputFile, settings); } + /// + /// Runs DotCover Report + /// for the specified action and settings. + /// + /// The context. + /// The DotCover coverage snapshot file name. + /// The settings. + /// + /// + /// DotCoverReport(new FilePath("./result.dcvr"), + /// new DotCoverReportSettings { + /// ReportType = DotCoverReportType.HTML + /// }); + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Report")] + [CakeNamespaceImport("Cake.Common.Tools.DotCover.Report")] + public static void DotCoverReport( + this ICakeContext context, + FilePath sourceFile, + DotCoverReportSettings settings) + { + ArgumentNullException.ThrowIfNull(context); + + if (settings == null) + { + settings = new DotCoverReportSettings(); + } + + // Create the DotCover reporter. + var reporter = new DotCoverReporter( + context.FileSystem, context.Environment, + context.ProcessRunner, context.Tools); + + // Run DotCover report. + reporter.Report(sourceFile, settings); + } + /// /// Runs DotCover Merge /// for the specified action and settings. diff --git a/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs b/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs index 0e04d50b72..a44d6848a0 100644 --- a/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs +++ b/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Cake.Core.IO; + namespace Cake.Common.Tools.DotCover.Report { /// @@ -15,5 +17,29 @@ public sealed class DotCoverReportSettings : DotCoverSettings /// The Default value is . /// public DotCoverReportType ReportType { get; set; } + + /// + /// Gets the path to save a formatted JSON report. + /// This represents the --json-report-output option. + /// + public FilePath JsonReportOutput { get; set; } + + /// + /// Gets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] + /// This represents the --json-report-covering-tests-scope option. + /// + public DotCoverReportScope? JsonReportCoveringTestsScope { get; set; } + + /// + /// Gets the path to save a formatted JSON report. + /// This represents the --xml-report-output option. + /// + public FilePath XmlReportOutput { get; set; } + + /// + /// Gets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] + /// This represents the --xml-report-covering-tests-scope option. + /// + public DotCoverReportScope? XmlReportCoveringTestsScope { get; set; } } -} +} \ No newline at end of file diff --git a/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs b/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs index 870ed0a1a9..90eed927bf 100644 --- a/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs +++ b/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs @@ -44,25 +44,91 @@ public void Report( DotCoverReportSettings settings) { ArgumentNullException.ThrowIfNull(sourceFile); - ArgumentNullException.ThrowIfNull(outputFile); ArgumentNullException.ThrowIfNull(settings); + if (settings.UseLegacySyntax) + { + ArgumentNullException.ThrowIfNull(outputFile); + } + // Run the tool. - Run(settings, GetArguments(sourceFile, outputFile, settings)); + Run(settings, GetArguments(sourceFile, settings, outputFile)); } - private ProcessArgumentBuilder GetArguments( + /// + /// Runs DotCover Cover with the specified settings. + /// + /// The DotCover coverage snapshot file name. + /// The settings. + public void Report( FilePath sourceFile, - FilePath outputFile, DotCoverReportSettings settings) + { + ArgumentNullException.ThrowIfNull(sourceFile); + ArgumentNullException.ThrowIfNull(settings); + + // Run the tool. + Run(settings, GetArguments(sourceFile, settings)); + } + + private ProcessArgumentBuilder GetArguments( + FilePath sourceFile, + DotCoverReportSettings settings, FilePath outputFile = null) { var builder = new ProcessArgumentBuilder(); - builder.Append("Report"); + builder.Append("report"); // Set configuration file if exists. GetConfigurationFileArgument(settings).CopyTo(builder); + if (settings.UseLegacySyntax) + { + GenerateLegacyArguments(sourceFile, outputFile, settings, builder); + } + else + { + GenerateArguments(sourceFile, settings, builder); + } + + // Get Global settings + GetArguments(settings).CopyTo(builder); + + return builder; + } + + private void GenerateArguments(FilePath sourceFile, DotCoverReportSettings settings, ProcessArgumentBuilder builder) + { + // Set the Source file. + builder.AppendSwitch("--snapshot-source", sourceFile.MakeAbsolute(_environment).FullPath.Quote()); + + // Set Json report output + if (settings.JsonReportOutput != null) + { + builder.AppendSwitch("--json-report-output", settings.JsonReportOutput.MakeAbsolute(_environment).FullPath.Quote()); + } + + // Set test scope, ignore default value + if (settings.JsonReportCoveringTestsScope.HasValue) + { + builder.AppendSwitch("--json-report-covering-tests-scope", settings.JsonReportCoveringTestsScope.Value.ToString().ToLowerInvariant().Quote()); + } + + // Set Xml report output + if (settings.XmlReportOutput != null) + { + builder.AppendSwitch("--xml-report-output", settings.XmlReportOutput.MakeAbsolute(_environment).FullPath.Quote()); + } + + // Set test scope, ignore default value + if (settings.XmlReportCoveringTestsScope.HasValue) + { + builder.AppendSwitch("--xml-report-covering-tests-scope", settings.XmlReportCoveringTestsScope.Value.ToString().ToLowerInvariant().Quote()); + } + } + + private void GenerateLegacyArguments(FilePath sourceFile, FilePath outputFile, DotCoverReportSettings settings, ProcessArgumentBuilder builder) + { // Set the Source file. sourceFile = sourceFile.MakeAbsolute(_environment); builder.AppendSwitch("/Source", "=", sourceFile.FullPath.Quote()); @@ -76,11 +142,6 @@ private ProcessArgumentBuilder GetArguments( { builder.AppendSwitch("/ReportType", "=", settings.ReportType.ToString()); } - - // Get Global settings - GetArguments(settings).CopyTo(builder); - - return builder; } } } From 8182caaa762eebb4ad8077da2ea20a6ae73c367b Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Wed, 17 Jun 2026 16:10:06 +0200 Subject: [PATCH 16/20] feat: fix lint errors --- .../Unit/Tools/DotCover/Analyse/DotCoverAnalyserTests.cs | 1 + .../Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs | 1 + .../Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs | 3 ++- .../Unit/Tools/DotCover/Report/DotCoverReporterTests.cs | 2 +- .../Tools/DotCover/Report/DotCoverReportSettings.cs | 8 ++++---- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Analyse/DotCoverAnalyserTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Analyse/DotCoverAnalyserTests.cs index d3a39025ac..b2b5e3f010 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Analyse/DotCoverAnalyserTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Analyse/DotCoverAnalyserTests.cs @@ -241,6 +241,7 @@ public void Should_Append_LogFile() // Given var fixture = new DotCoverAnalyserFixture(); fixture.Settings.LogFile = "./logfile.log"; + fixture.Settings.UseLegacySyntax = true; // When var result = fixture.Run(); diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs index 5f76af5c60..9363875893 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs @@ -9,6 +9,7 @@ using Cake.Common.Tools.XUnit; using Cake.Core.IO; using Cake.Testing; +using Xunit; namespace Cake.Common.Tests.Unit.Tools.DotCover.Cover { diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs index 04b9a4a0a6..2f3f4d7040 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Merge/DotCoverMergerTests.cs @@ -2,10 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using Cake.Common.Tests.Fixtures.Tools.DotCover.Merge; using Cake.Common.Tools.DotCover; using Cake.Core.IO; - +using Xunit; namespace Cake.Common.Tests.Unit.Tools.DotCover.Merge { public sealed class DotCoverMergerTests diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs index 0b85476a44..3980f0d83b 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs @@ -5,6 +5,7 @@ using Cake.Common.Tests.Fixtures.Tools.DotCover.Report; using Cake.Common.Tools.DotCover; using Cake.Core.IO; +using Xunit; namespace Cake.Common.Tests.Unit.Tools.DotCover.Report { @@ -98,7 +99,6 @@ public void Should_Append_JsonReportScope(DotCoverReportScope reportScope, strin "--json-report-covering-tests-scope \"" + reportScopeString + "\"", result.Args); } - [Fact] public void Should_Append_XmlReportOutput() { diff --git a/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs b/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs index a44d6848a0..a1a98382c5 100644 --- a/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs +++ b/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs @@ -19,25 +19,25 @@ public sealed class DotCoverReportSettings : DotCoverSettings public DotCoverReportType ReportType { get; set; } /// - /// Gets the path to save a formatted JSON report. + /// Gets or sets the path to save a formatted JSON report. /// This represents the --json-report-output option. /// public FilePath JsonReportOutput { get; set; } /// - /// Gets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] + /// Gets or sets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] /// This represents the --json-report-covering-tests-scope option. /// public DotCoverReportScope? JsonReportCoveringTestsScope { get; set; } /// - /// Gets the path to save a formatted JSON report. + /// Gets or sets the path to save a formatted JSON report. /// This represents the --xml-report-output option. /// public FilePath XmlReportOutput { get; set; } /// - /// Gets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] + /// Gets or sets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] /// This represents the --xml-report-covering-tests-scope option. /// public DotCoverReportScope? XmlReportCoveringTestsScope { get; set; } From 76915aa9becacbd271c460784205eaae3e779ef7 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Tue, 11 Aug 2026 09:57:47 +0200 Subject: [PATCH 17/20] feat: DotCoverReport do not ignore outputFile in new syntax --- .../DotCover/Report/DotCoverReporterTests.cs | 34 +++++++++++++++++++ .../Tools/DotCover/Report/DotCoverReporter.cs | 17 ++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs index 3980f0d83b..9338b062b4 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Report/DotCoverReporterTests.cs @@ -58,6 +58,40 @@ public void Should_Ignore_Output_File_If_Null() "--snapshot-source \"/Working/result.dcvr\"", result.Args); } + [Fact] + public void Should_Not_Ignore_Output_File_With_New_Syntax() + { + // Given + var fixture = new DotCoverReporterFixture(); + fixture.OutputFile = "myoutputfile.xml"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("report " + + "--snapshot-source \"/Working/result.dcvr\" " + + "--xml-report-output \"/Working/myoutputfile.xml\"", result.Args); + } + + [Fact] + public void Should_Not_Ignore_Output_File_With_New_Syntax_Json() + { + // Given + var fixture = new DotCoverReporterFixture(); + fixture.OutputFile = "myoutputfile.json"; + fixture.Settings.ReportType = DotCoverReportType.JSON; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("report " + + "--snapshot-source \"/Working/result.dcvr\" " + + "--json-report-output \"/Working/myoutputfile.json\"", result.Args); + } + + [Fact] public void Should_Append_JsonReportOutput() { diff --git a/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs b/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs index 90eed927bf..38e33b4a08 100644 --- a/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs +++ b/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs @@ -51,6 +51,23 @@ public void Report( ArgumentNullException.ThrowIfNull(outputFile); } + if (!settings.UseLegacySyntax && outputFile != null) + { + // map outputFile to new syntax parameters. Otherwise input is ignored + switch (settings.ReportType) + { + case DotCoverReportType.XML: + settings.XmlReportOutput = outputFile; + break; + case DotCoverReportType.JSON: + settings.JsonReportOutput = outputFile; + break; + default: + settings.XmlReportOutput = outputFile; + break; + } + } + // Run the tool. Run(settings, GetArguments(sourceFile, settings, outputFile)); } From 941358c599bd66525c578c3ad391c1bd707b60e2 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Tue, 11 Aug 2026 10:01:03 +0200 Subject: [PATCH 18/20] feat: warning when legacy filters are dropped in new DotCover format --- .../DotCover/Cover/DotCoverCovererTests.cs | 8 ++++++ .../Tools/DotCover/Cover/DotCoverCoverer.cs | 26 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs index 9363875893..3be5387c9b 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs @@ -9,7 +9,10 @@ using Cake.Common.Tools.XUnit; using Cake.Core.IO; using Cake.Testing; +using NSubstitute; using Xunit; +using LogLevel = Cake.Core.Diagnostics.LogLevel; +using Verbosity = Cake.Core.Diagnostics.Verbosity; namespace Cake.Common.Tests.Unit.Tools.DotCover.Cover { @@ -158,6 +161,7 @@ public void Should_Not_Append_Scope() var result = fixture.Run(); // Then + fixture.Context.Log.Received(1).Write(Verbosity.Minimal, LogLevel.Warning, "{0}", "Scope parameter is not supported in new DotCover format"); Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + "--snapshot-output \"/Working/result.dcvr\"", result.Args); @@ -175,6 +179,7 @@ public void Should_Not_Append_Filters() var result = fixture.Run(); // Then + fixture.Context.Log.Received(1).Write(Verbosity.Minimal, LogLevel.Warning, "{0}", "Filters parameter is not supported in new DotCover format"); Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + "--snapshot-output \"/Working/result.dcvr\"", result.Args); @@ -192,6 +197,7 @@ public void Should_Not_Append_AttributeFilters() var result = fixture.Run(); // Then + fixture.Context.Log.Received(1).Write(Verbosity.Minimal, LogLevel.Warning, "{0}", "AttributeFilters parameter is not supported in new DotCover format"); Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + "--snapshot-output \"/Working/result.dcvr\"", result.Args); @@ -208,6 +214,7 @@ public void Should_Not_Append_DisableDefaultFilters() var result = fixture.Run(); // Then + fixture.Context.Log.Received(1).Write(Verbosity.Minimal, LogLevel.Warning, "{0}", "DisableDefaultFilters parameter is not supported in new DotCover format"); Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + "--snapshot-output \"/Working/result.dcvr\"", result.Args); @@ -225,6 +232,7 @@ public void Should_Not_Append_ProcessFilters() var result = fixture.Run(); // Then + fixture.Context.Log.Received(1).Write(Verbosity.Minimal, LogLevel.Warning, "{0}", "ProcessFilters parameter is not supported in new DotCover format"); Assert.Equal("cover --target-executable \"/Working/tools/Test.exe\" " + "--target-arguments \"-argument\" " + "--snapshot-output \"/Working/result.dcvr\"", result.Args); diff --git a/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs b/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs index 96e5339908..354229f392 100644 --- a/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs +++ b/src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs @@ -4,6 +4,7 @@ using System; using Cake.Core; +using Cake.Core.Diagnostics; using Cake.Core.IO; using Cake.Core.Tooling; @@ -124,6 +125,31 @@ private ProcessArgumentBuilder GetArguments( builder.Append("--no-ngen"); } + if (settings.Filters is { Count: > 0 }) + { + context.Log.Warning("Filters parameter is not supported in new DotCover format"); + } + + if (settings.Scope is { Count: > 0 }) + { + context.Log.Warning("Scope parameter is not supported in new DotCover format"); + } + + if (settings.AttributeFilters is { Count: > 0 }) + { + context.Log.Warning("AttributeFilters parameter is not supported in new DotCover format"); + } + + if (settings.ProcessFilters is { Count: > 0 }) + { + context.Log.Warning("ProcessFilters parameter is not supported in new DotCover format"); + } + + if (settings.DisableDefaultFilters is true) + { + context.Log.Warning("DisableDefaultFilters parameter is not supported in new DotCover format"); + } + // Get base arguments - new format GetCoverArguments(settings).CopyTo(builder); } From e77b2bba8cbb18eb361c7e69ec0df609c312a485 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Tue, 11 Aug 2026 10:03:43 +0200 Subject: [PATCH 19/20] feat: changed dotcover report examples to XML rather then HTML --- src/Cake.Common/Tools/DotCover/DotCoverAliases.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs b/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs index be0e8796c9..fad9b4aec4 100644 --- a/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs +++ b/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs @@ -132,9 +132,9 @@ public static void DotCoverCover( /// /// /// DotCoverReport(new FilePath("./result.dcvr"), - /// new FilePath("./result.html"), + /// new FilePath("./result.xml"), /// new DotCoverReportSettings { - /// ReportType = DotCoverReportType.HTML + /// ReportType = DotCoverReportType.XML /// }); /// /// @@ -174,7 +174,7 @@ public static void DotCoverReport( /// /// DotCoverReport(new FilePath("./result.dcvr"), /// new DotCoverReportSettings { - /// ReportType = DotCoverReportType.HTML + /// ReportType = DotCoverReportType.Xml /// }); /// /// From 0863ad0c03ded2aefb89b29fa78c0a4bdfd68c08 Mon Sep 17 00:00:00 2001 From: Flavio Lazzarini Date: Tue, 11 Aug 2026 10:13:34 +0200 Subject: [PATCH 20/20] feat: fixed typos and added overload for DotCoverMerge alias --- .../DotCover/Cover/DotCoverCovererTests.cs | 4 +- .../Tools/DotCover/DotCoverAliases.cs | 51 +++++++++++++++++++ .../DotCover/Report/DotCoverReportSettings.cs | 4 +- .../Tools/DotCover/Report/DotCoverReporter.cs | 4 +- 4 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs index 3be5387c9b..3f042bea32 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs @@ -90,7 +90,7 @@ public void Should_Throw_If_No_Tool_Was_Intercepted() AssertEx.IsCakeException(result, "No tool was started."); } - #region New Paramter Syntax + #region New Parameter Syntax [Fact] public void Should_Capture_Tool_And_Arguments_From_Action() @@ -542,7 +542,7 @@ public void Should_Support_New_Features_In_New_Mode() #endregion - #region Legacy Paramter Syntax + #region Legacy Parameter Syntax [Fact] public void Should_Capture_Tool_And_Arguments_From_Action_LegacySyntax() diff --git a/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs b/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs index fad9b4aec4..b959c04e6b 100644 --- a/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs +++ b/src/Cake.Common/Tools/DotCover/DotCoverAliases.cs @@ -273,5 +273,56 @@ public static void DotCoverMerge( // Run DotCover report. merger.Merge(sourceFiles, outputFile, settings); } + + /// + /// Runs DotCover Merge + /// for the specified action and settings. + /// + /// The context. + /// The list of DotCover coverage snapshot files. + /// + /// + /// DotCoverMerge(new[] { + /// new FilePath("./result1.dcvr"), + /// new FilePath("./result2.dcvr") + /// }); + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Merge")] + [CakeNamespaceImport("Cake.Common.Tools.DotCover.Merge")] + public static void DotCoverMerge( + this ICakeContext context, + IEnumerable sourceFiles) + { + DotCoverMerge(context, sourceFiles, new DotCoverMergeSettings()); + } + + /// + /// Runs DotCover Merge + /// for the specified action and settings. + /// + /// The context. + /// The list of DotCover coverage snapshot files. + /// The settings. + /// + /// + /// DotCoverMerge(new[] { + /// new FilePath("./result1.dcvr"), + /// new FilePath("./result2.dcvr") + /// }, + /// new DotCoverMergeSettings()); + /// + /// + public static void DotCoverMerge( + this ICakeContext context, + IEnumerable sourceFiles, + DotCoverMergeSettings settings) + { + var merger = new DotCoverMerger( + context.FileSystem, context.Environment, + context.ProcessRunner, context.Tools); + merger.Merge(sourceFiles, settings); + } } } \ No newline at end of file diff --git a/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs b/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs index a1a98382c5..23915dd7c8 100644 --- a/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs +++ b/src/Cake.Common/Tools/DotCover/Report/DotCoverReportSettings.cs @@ -31,13 +31,13 @@ public sealed class DotCoverReportSettings : DotCoverSettings public DotCoverReportScope? JsonReportCoveringTestsScope { get; set; } /// - /// Gets or sets the path to save a formatted JSON report. + /// Gets or sets the path to save a formatted XML report. /// This represents the --xml-report-output option. /// public FilePath XmlReportOutput { get; set; } /// - /// Gets or sets granularity for including covering tests in JSON reports: [none|assembly|type|method|statement] + /// Gets or sets granularity for including covering tests in XML reports: [none|assembly|type|method|statement] /// This represents the --xml-report-covering-tests-scope option. /// public DotCoverReportScope? XmlReportCoveringTestsScope { get; set; } diff --git a/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs b/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs index 38e33b4a08..3485281f4a 100644 --- a/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs +++ b/src/Cake.Common/Tools/DotCover/Report/DotCoverReporter.cs @@ -33,7 +33,7 @@ public DotCoverReporter( } /// - /// Runs DotCover Cover with the specified settings. + /// Runs DotCover Report with the specified settings. /// /// The DotCover coverage snapshot file name. /// The DotCover output file. @@ -73,7 +73,7 @@ public void Report( } /// - /// Runs DotCover Cover with the specified settings. + /// Runs DotCover Report with the specified settings. /// /// The DotCover coverage snapshot file name. /// The settings.