Skip to content

Commit 2e9ef34

Browse files
Copilotjarlehjortland
authored andcommitted
Update DotCover implementation to use new command format and add support for new options
Co-authored-by: jarlehjortland <[email protected]>
1 parent af6ab79 commit 2e9ef34

File tree

8 files changed

+739
-44
lines changed

8 files changed

+739
-44
lines changed

src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs

Lines changed: 274 additions & 31 deletions
Large diffs are not rendered by default.

src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverSettings.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,63 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using Cake.Core.IO;
6+
57
namespace Cake.Common.Tools.DotCover.Cover
68
{
79
/// <summary>
810
/// Contains settings used by <see cref="DotCoverCoverer" />.
911
/// </summary>
1012
public sealed class DotCoverCoverSettings : DotCoverCoverageSettings
1113
{
14+
/// <summary>
15+
/// Gets or sets the path to save formatted JSON report.
16+
/// This represents the <c>--json-report-output</c> option.
17+
/// </summary>
18+
public FilePath JsonReportOutput { get; set; }
19+
20+
/// <summary>
21+
/// Gets or sets the granularity for including covering tests in JSON reports.
22+
/// This represents the <c>--json-report-covering-tests-scope</c> option.
23+
/// </summary>
24+
public DotCoverReportScope? JsonReportCoveringTestsScope { get; set; }
25+
26+
/// <summary>
27+
/// Gets or sets the path to save formatted XML report.
28+
/// This represents the <c>--xml-report-output</c> option.
29+
/// </summary>
30+
public FilePath XmlReportOutput { get; set; }
31+
32+
/// <summary>
33+
/// Gets or sets the granularity for including covering tests in XML reports.
34+
/// This represents the <c>--xml-report-covering-tests-scope</c> option.
35+
/// </summary>
36+
public DotCoverReportScope? XmlReportCoveringTestsScope { get; set; }
37+
38+
/// <summary>
39+
/// Gets or sets the directory for temporary files.
40+
/// This represents the <c>--temporary-directory</c> option.
41+
/// </summary>
42+
public DirectoryPath TemporaryDirectory { get; set; }
43+
44+
/// <summary>
45+
/// Gets or sets a value indicating whether to control the coverage session using the profiler API.
46+
/// This represents the <c>--use-api</c> option.
47+
/// </summary>
48+
public bool UseApi { get; set; }
49+
50+
/// <summary>
51+
/// Gets or sets a value indicating whether to disable loading of NGen images during coverage.
52+
/// This represents the <c>--no-ngen</c> option.
53+
/// </summary>
54+
public bool NoNGen { get; set; }
55+
56+
/// <summary>
57+
/// Gets or sets a value indicating whether to use the legacy command syntax.
58+
/// When true, uses old format like '/TargetExecutable="/path"'.
59+
/// When false, uses new format like '--target-executable "/path"'.
60+
/// Default is false (new format).
61+
/// </summary>
62+
public bool UseLegacySyntax { get; set; }
1263
}
1364
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using Cake.Core.IO;
7+
8+
namespace Cake.Common.Tools.DotCover.Cover
9+
{
10+
/// <summary>
11+
/// Contains extensions for <see cref="DotCoverCoverSettings"/>.
12+
/// </summary>
13+
public static class DotCoverCoverSettingsExtensions
14+
{
15+
/// <summary>
16+
/// Sets the JSON report output path.
17+
/// </summary>
18+
/// <param name="settings">The settings.</param>
19+
/// <param name="outputPath">The JSON report output path.</param>
20+
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
21+
public static DotCoverCoverSettings WithJsonReportOutput(this DotCoverCoverSettings settings, FilePath outputPath)
22+
{
23+
ArgumentNullException.ThrowIfNull(settings);
24+
settings.JsonReportOutput = outputPath;
25+
return settings;
26+
}
27+
28+
/// <summary>
29+
/// Sets the JSON report covering tests scope.
30+
/// </summary>
31+
/// <param name="settings">The settings.</param>
32+
/// <param name="scope">The granularity for including covering tests in JSON reports.</param>
33+
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
34+
public static DotCoverCoverSettings WithJsonReportCoveringTestsScope(this DotCoverCoverSettings settings, DotCoverReportScope scope)
35+
{
36+
ArgumentNullException.ThrowIfNull(settings);
37+
settings.JsonReportCoveringTestsScope = scope;
38+
return settings;
39+
}
40+
41+
/// <summary>
42+
/// Sets the XML report output path.
43+
/// </summary>
44+
/// <param name="settings">The settings.</param>
45+
/// <param name="outputPath">The XML report output path.</param>
46+
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
47+
public static DotCoverCoverSettings WithXmlReportOutput(this DotCoverCoverSettings settings, FilePath outputPath)
48+
{
49+
ArgumentNullException.ThrowIfNull(settings);
50+
settings.XmlReportOutput = outputPath;
51+
return settings;
52+
}
53+
54+
/// <summary>
55+
/// Sets the XML report covering tests scope.
56+
/// </summary>
57+
/// <param name="settings">The settings.</param>
58+
/// <param name="scope">The granularity for including covering tests in XML reports.</param>
59+
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
60+
public static DotCoverCoverSettings WithXmlReportCoveringTestsScope(this DotCoverCoverSettings settings, DotCoverReportScope scope)
61+
{
62+
ArgumentNullException.ThrowIfNull(settings);
63+
settings.XmlReportCoveringTestsScope = scope;
64+
return settings;
65+
}
66+
67+
/// <summary>
68+
/// Sets the temporary directory for files.
69+
/// </summary>
70+
/// <param name="settings">The settings.</param>
71+
/// <param name="directory">The temporary directory path.</param>
72+
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
73+
public static DotCoverCoverSettings WithTemporaryDirectory(this DotCoverCoverSettings settings, DirectoryPath directory)
74+
{
75+
ArgumentNullException.ThrowIfNull(settings);
76+
settings.TemporaryDirectory = directory;
77+
return settings;
78+
}
79+
80+
/// <summary>
81+
/// Enables control of the coverage session using the profiler API.
82+
/// </summary>
83+
/// <param name="settings">The settings.</param>
84+
/// <param name="useApi">Whether to use the API.</param>
85+
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
86+
public static DotCoverCoverSettings WithUseApi(this DotCoverCoverSettings settings, bool useApi = true)
87+
{
88+
ArgumentNullException.ThrowIfNull(settings);
89+
settings.UseApi = useApi;
90+
return settings;
91+
}
92+
93+
/// <summary>
94+
/// Disables loading of NGen images during coverage.
95+
/// </summary>
96+
/// <param name="settings">The settings.</param>
97+
/// <param name="noNGen">Whether to disable NGen.</param>
98+
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
99+
public static DotCoverCoverSettings WithNoNGen(this DotCoverCoverSettings settings, bool noNGen = true)
100+
{
101+
ArgumentNullException.ThrowIfNull(settings);
102+
settings.NoNGen = noNGen;
103+
return settings;
104+
}
105+
106+
/// <summary>
107+
/// Configures whether to use legacy command syntax.
108+
/// </summary>
109+
/// <param name="settings">The settings.</param>
110+
/// <param name="useLegacySyntax">Whether to use legacy syntax. Default is false (new syntax).</param>
111+
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
112+
public static DotCoverCoverSettings WithLegacySyntax(this DotCoverCoverSettings settings, bool useLegacySyntax = true)
113+
{
114+
ArgumentNullException.ThrowIfNull(settings);
115+
settings.UseLegacySyntax = useLegacySyntax;
116+
return settings;
117+
}
118+
}
119+
}

src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs

Lines changed: 150 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,163 @@ private ProcessArgumentBuilder GetArguments(
6161
{
6262
var builder = new ProcessArgumentBuilder();
6363

64-
builder.Append("Cover");
64+
// Command name - always lowercase 'cover' for both formats
65+
builder.Append("cover");
6566

6667
// Set configuration file if exists.
6768
GetConfigurationFileArgument(settings).CopyTo(builder);
6869

69-
// Get Target executable arguments
70-
GetTargetArguments(context, action).CopyTo(builder);
70+
if (settings.UseLegacySyntax)
71+
{
72+
// Use legacy format
73+
GetTargetArguments(context, action).CopyTo(builder);
74+
// Set the output file - legacy format
75+
outputPath = outputPath.MakeAbsolute(_environment);
76+
builder.AppendSwitch("/Output", "=", outputPath.FullPath.Quote());
77+
// Get Coverage arguments - legacy format
78+
GetCoverageArguments(settings).CopyTo(builder);
79+
// Get base arguments - legacy format
80+
GetArguments(settings).CopyTo(builder);
81+
}
82+
else
83+
{
84+
// Use new format
85+
GetCoverTargetArguments(context, action).CopyTo(builder);
86+
// Set the output file - new format
87+
outputPath = outputPath.MakeAbsolute(_environment);
88+
builder.AppendSwitch("--snapshot-output", outputPath.FullPath.Quote());
89+
// Get Coverage arguments - new format
90+
GetCoverCoverageArguments(settings).CopyTo(builder);
91+
// New report options (only available in new format)
92+
if (settings.JsonReportOutput != null)
93+
{
94+
builder.AppendSwitch("--json-report-output", settings.JsonReportOutput.MakeAbsolute(_environment).FullPath.Quote());
95+
}
7196

72-
// Set the output file.
73-
outputPath = outputPath.MakeAbsolute(_environment);
74-
builder.AppendSwitch("/Output", "=", outputPath.FullPath.Quote());
97+
if (settings.JsonReportCoveringTestsScope.HasValue)
98+
{
99+
builder.AppendSwitch("--json-report-covering-tests-scope", settings.JsonReportCoveringTestsScope.Value.ToString().ToLowerInvariant().Quote());
100+
}
75101

76-
// Get Coverage arguments
77-
GetCoverageArguments(settings).CopyTo(builder);
102+
if (settings.XmlReportOutput != null)
103+
{
104+
builder.AppendSwitch("--xml-report-output", settings.XmlReportOutput.MakeAbsolute(_environment).FullPath.Quote());
105+
}
78106

79-
// Get base arguments
80-
GetArguments(settings).CopyTo(builder);
107+
if (settings.XmlReportCoveringTestsScope.HasValue)
108+
{
109+
builder.AppendSwitch("--xml-report-covering-tests-scope", settings.XmlReportCoveringTestsScope.Value.ToString().ToLowerInvariant().Quote());
110+
}
111+
112+
if (settings.TemporaryDirectory != null)
113+
{
114+
builder.AppendSwitch("--temporary-directory", settings.TemporaryDirectory.MakeAbsolute(_environment).FullPath.Quote());
115+
}
116+
117+
if (settings.UseApi)
118+
{
119+
builder.Append("--use-api");
120+
}
121+
122+
if (settings.NoNGen)
123+
{
124+
builder.Append("--no-ngen");
125+
}
126+
127+
// Get base arguments - new format
128+
GetCoverArguments(settings).CopyTo(builder);
129+
}
130+
131+
return builder;
132+
}
133+
134+
/// <summary>
135+
/// Get arguments from coverage settings for Cover command (using new format).
136+
/// </summary>
137+
/// <param name="settings">The settings.</param>
138+
/// <returns>The process arguments.</returns>
139+
private ProcessArgumentBuilder GetCoverCoverageArguments(DotCoverCoverageSettings settings)
140+
{
141+
var builder = new ProcessArgumentBuilder();
142+
143+
// TargetWorkingDir - using new format for Cover command
144+
if (settings.TargetWorkingDir != null)
145+
{
146+
builder.AppendSwitch("--target-working-directory", settings.TargetWorkingDir.MakeAbsolute(_environment).FullPath.Quote());
147+
}
148+
149+
// New filtering options (only available in new format)
150+
if (settings.ExcludeAssemblies.Count > 0)
151+
{
152+
var excludeAssemblies = string.Join(',', settings.ExcludeAssemblies);
153+
builder.AppendSwitch("--exclude-assemblies", excludeAssemblies.Quote());
154+
}
155+
156+
if (settings.ExcludeAttributes.Count > 0)
157+
{
158+
var excludeAttributes = string.Join(',', settings.ExcludeAttributes);
159+
builder.AppendSwitch("--exclude-attributes", excludeAttributes.Quote());
160+
}
161+
162+
if (settings.ExcludeProcesses.Count > 0)
163+
{
164+
var excludeProcesses = string.Join(',', settings.ExcludeProcesses);
165+
builder.AppendSwitch("--exclude-processes", excludeProcesses.Quote());
166+
}
167+
168+
// Legacy filtering options (maintain backward compatibility with old format)
169+
// Scope
170+
if (settings.Scope.Count > 0)
171+
{
172+
var scope = string.Join(';', settings.Scope);
173+
builder.AppendSwitch("/Scope", "=", scope.Quote());
174+
}
175+
176+
// Filters
177+
if (settings.Filters.Count > 0)
178+
{
179+
var filters = string.Join(';', settings.Filters);
180+
builder.AppendSwitch("/Filters", "=", filters.Quote());
181+
}
182+
183+
// AttributeFilters
184+
if (settings.AttributeFilters.Count > 0)
185+
{
186+
var attributeFilters = string.Join(';', settings.AttributeFilters);
187+
builder.AppendSwitch("/AttributeFilters", "=", attributeFilters.Quote());
188+
}
189+
190+
// ProcessFilters
191+
if (settings.ProcessFilters.Count > 0)
192+
{
193+
var processFilters = string.Join(';', settings.ProcessFilters);
194+
builder.AppendSwitch("/ProcessFilters", "=", processFilters.Quote());
195+
}
196+
197+
// DisableDefaultFilters
198+
if (settings.DisableDefaultFilters)
199+
{
200+
builder.Append("/DisableDefaultFilters");
201+
}
202+
203+
return builder;
204+
}
205+
206+
/// <summary>
207+
/// Get arguments from global settings for Cover command (using new format).
208+
/// </summary>
209+
/// <param name="settings">The settings.</param>
210+
/// <returns>The process arguments.</returns>
211+
private ProcessArgumentBuilder GetCoverArguments(DotCoverSettings settings)
212+
{
213+
var builder = new ProcessArgumentBuilder();
214+
215+
// LogFile - using new format for Cover command
216+
if (settings.LogFile != null)
217+
{
218+
var logFilePath = settings.LogFile.MakeAbsolute(_environment);
219+
builder.AppendSwitch("--log-file", logFilePath.FullPath.Quote());
220+
}
81221

82222
return builder;
83223
}

0 commit comments

Comments
 (0)