Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
305 changes: 274 additions & 31 deletions src/Cake.Common.Tests/Unit/Tools/DotCover/Cover/DotCoverCovererTests.cs

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,63 @@
// 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.Cover
{
/// <summary>
/// Contains settings used by <see cref="DotCoverCoverer" />.
/// </summary>
public sealed class DotCoverCoverSettings : DotCoverCoverageSettings
{
/// <summary>
/// Gets or sets the path to save formatted JSON report.
/// This represents the <c>--json-report-output</c> option.
/// </summary>
public FilePath JsonReportOutput { get; set; }

/// <summary>
/// Gets or sets the granularity for including covering tests in JSON reports.
/// This represents the <c>--json-report-covering-tests-scope</c> option.
/// </summary>
public DotCoverReportScope? JsonReportCoveringTestsScope { get; set; }

/// <summary>
/// Gets or sets the path to save formatted XML report.
/// This represents the <c>--xml-report-output</c> option.
/// </summary>
public FilePath XmlReportOutput { get; set; }

/// <summary>
/// Gets or sets the granularity for including covering tests in XML reports.
/// This represents the <c>--xml-report-covering-tests-scope</c> option.
/// </summary>
public DotCoverReportScope? XmlReportCoveringTestsScope { get; set; }

/// <summary>
/// Gets or sets the directory for temporary files.
/// This represents the <c>--temporary-directory</c> option.
/// </summary>
public DirectoryPath TemporaryDirectory { get; set; }

/// <summary>
/// Gets or sets a value indicating whether to control the coverage session using the profiler API.
/// This represents the <c>--use-api</c> option.
/// </summary>
public bool UseApi { get; set; }

/// <summary>
/// Gets or sets a value indicating whether to disable loading of NGen images during coverage.
/// This represents the <c>--no-ngen</c> option.
/// </summary>
public bool NoNGen { get; set; }

/// <summary>
/// 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).
/// </summary>
public bool UseLegacySyntax { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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;
using Cake.Core.IO;

namespace Cake.Common.Tools.DotCover.Cover
{
/// <summary>
/// Contains extensions for <see cref="DotCoverCoverSettings"/>.
/// </summary>
public static class DotCoverCoverSettingsExtensions
{
/// <summary>
/// Sets the JSON report output path.
/// </summary>
/// <param name="settings">The settings.</param>
/// <param name="outputPath">The JSON report output path.</param>
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
public static DotCoverCoverSettings WithJsonReportOutput(this DotCoverCoverSettings settings, FilePath outputPath)
{
ArgumentNullException.ThrowIfNull(settings);
settings.JsonReportOutput = outputPath;
return settings;
}

/// <summary>
/// Sets the JSON report covering tests scope.
/// </summary>
/// <param name="settings">The settings.</param>
/// <param name="scope">The granularity for including covering tests in JSON reports.</param>
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
public static DotCoverCoverSettings WithJsonReportCoveringTestsScope(this DotCoverCoverSettings settings, DotCoverReportScope scope)
{
ArgumentNullException.ThrowIfNull(settings);
settings.JsonReportCoveringTestsScope = scope;
return settings;
}

/// <summary>
/// Sets the XML report output path.
/// </summary>
/// <param name="settings">The settings.</param>
/// <param name="outputPath">The XML report output path.</param>
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
public static DotCoverCoverSettings WithXmlReportOutput(this DotCoverCoverSettings settings, FilePath outputPath)
{
ArgumentNullException.ThrowIfNull(settings);
settings.XmlReportOutput = outputPath;
return settings;
}

/// <summary>
/// Sets the XML report covering tests scope.
/// </summary>
/// <param name="settings">The settings.</param>
/// <param name="scope">The granularity for including covering tests in XML reports.</param>
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
public static DotCoverCoverSettings WithXmlReportCoveringTestsScope(this DotCoverCoverSettings settings, DotCoverReportScope scope)
{
ArgumentNullException.ThrowIfNull(settings);
settings.XmlReportCoveringTestsScope = scope;
return settings;
}

/// <summary>
/// Sets the temporary directory for files.
/// </summary>
/// <param name="settings">The settings.</param>
/// <param name="directory">The temporary directory path.</param>
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
public static DotCoverCoverSettings WithTemporaryDirectory(this DotCoverCoverSettings settings, DirectoryPath directory)
{
ArgumentNullException.ThrowIfNull(settings);
settings.TemporaryDirectory = directory;
return settings;
}

/// <summary>
/// Enables control of the coverage session using the profiler API.
/// </summary>
/// <param name="settings">The settings.</param>
/// <param name="useApi">Whether to use the API.</param>
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
public static DotCoverCoverSettings WithUseApi(this DotCoverCoverSettings settings, bool useApi = true)
{
ArgumentNullException.ThrowIfNull(settings);
settings.UseApi = useApi;
return settings;
}

/// <summary>
/// Disables loading of NGen images during coverage.
/// </summary>
/// <param name="settings">The settings.</param>
/// <param name="noNGen">Whether to disable NGen.</param>
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
public static DotCoverCoverSettings WithNoNGen(this DotCoverCoverSettings settings, bool noNGen = true)
{
ArgumentNullException.ThrowIfNull(settings);
settings.NoNGen = noNGen;
return settings;
}

/// <summary>
/// Configures whether to use legacy command syntax.
/// </summary>
/// <param name="settings">The settings.</param>
/// <param name="useLegacySyntax">Whether to use legacy syntax. Default is false (new syntax).</param>
/// <returns>The same <see cref="DotCoverCoverSettings"/> instance so that multiple calls can be chained.</returns>
public static DotCoverCoverSettings WithLegacySyntax(this DotCoverCoverSettings settings, bool useLegacySyntax = true)
{
ArgumentNullException.ThrowIfNull(settings);
settings.UseLegacySyntax = useLegacySyntax;
return settings;
}
}
}
160 changes: 150 additions & 10 deletions src/Cake.Common/Tools/DotCover/Cover/DotCoverCoverer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,23 +61,163 @@ private ProcessArgumentBuilder GetArguments(
{
var builder = new ProcessArgumentBuilder();

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

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

// Get Target executable arguments
GetTargetArguments(context, action).CopyTo(builder);
if (settings.UseLegacySyntax)
{
// Use legacy format
GetTargetArguments(context, action).CopyTo(builder);
// Set the output file - legacy format
outputPath = outputPath.MakeAbsolute(_environment);
builder.AppendSwitch("/Output", "=", outputPath.FullPath.Quote());
// Get Coverage arguments - legacy format
GetCoverageArguments(settings).CopyTo(builder);
// Get base arguments - legacy format
GetArguments(settings).CopyTo(builder);
}
else
{
// Use new format
GetCoverTargetArguments(context, action).CopyTo(builder);
// Set the output file - new format
outputPath = outputPath.MakeAbsolute(_environment);
builder.AppendSwitch("--snapshot-output", outputPath.FullPath.Quote());
// Get Coverage arguments - new format
GetCoverCoverageArguments(settings).CopyTo(builder);
// New report options (only available in new format)
if (settings.JsonReportOutput != null)
{
builder.AppendSwitch("--json-report-output", settings.JsonReportOutput.MakeAbsolute(_environment).FullPath.Quote());
}

// Set the output file.
outputPath = outputPath.MakeAbsolute(_environment);
builder.AppendSwitch("/Output", "=", outputPath.FullPath.Quote());
if (settings.JsonReportCoveringTestsScope.HasValue)
{
builder.AppendSwitch("--json-report-covering-tests-scope", settings.JsonReportCoveringTestsScope.Value.ToString().ToLowerInvariant().Quote());
}

// Get Coverage arguments
GetCoverageArguments(settings).CopyTo(builder);
if (settings.XmlReportOutput != null)
{
builder.AppendSwitch("--xml-report-output", settings.XmlReportOutput.MakeAbsolute(_environment).FullPath.Quote());
}

// Get base arguments
GetArguments(settings).CopyTo(builder);
if (settings.XmlReportCoveringTestsScope.HasValue)
{
builder.AppendSwitch("--xml-report-covering-tests-scope", settings.XmlReportCoveringTestsScope.Value.ToString().ToLowerInvariant().Quote());
}

if (settings.TemporaryDirectory != null)
{
builder.AppendSwitch("--temporary-directory", settings.TemporaryDirectory.MakeAbsolute(_environment).FullPath.Quote());
}

if (settings.UseApi)
{
builder.Append("--use-api");
}

if (settings.NoNGen)
{
builder.Append("--no-ngen");
}

// Get base arguments - new format
GetCoverArguments(settings).CopyTo(builder);
}

return builder;
}

/// <summary>
/// Get arguments from coverage settings for Cover command (using new format).
/// </summary>
/// <param name="settings">The settings.</param>
/// <returns>The process arguments.</returns>
private ProcessArgumentBuilder GetCoverCoverageArguments(DotCoverCoverageSettings settings)
{
var builder = new ProcessArgumentBuilder();

// TargetWorkingDir - using new format for Cover command
if (settings.TargetWorkingDir != null)
{
builder.AppendSwitch("--target-working-directory", settings.TargetWorkingDir.MakeAbsolute(_environment).FullPath.Quote());
}

// New filtering options (only available in new format)
if (settings.ExcludeAssemblies.Count > 0)
{
var excludeAssemblies = string.Join(',', settings.ExcludeAssemblies);
builder.AppendSwitch("--exclude-assemblies", excludeAssemblies.Quote());
}

if (settings.ExcludeAttributes.Count > 0)
{
var excludeAttributes = string.Join(',', settings.ExcludeAttributes);
builder.AppendSwitch("--exclude-attributes", excludeAttributes.Quote());
}

if (settings.ExcludeProcesses.Count > 0)
{
var excludeProcesses = string.Join(',', settings.ExcludeProcesses);
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;
}

/// <summary>
/// Get arguments from global settings for Cover command (using new format).
/// </summary>
/// <param name="settings">The settings.</param>
/// <returns>The process arguments.</returns>
private ProcessArgumentBuilder GetCoverArguments(DotCoverSettings settings)
{
var builder = new ProcessArgumentBuilder();

// LogFile - using new format for Cover command
if (settings.LogFile != null)
{
var logFilePath = settings.LogFile.MakeAbsolute(_environment);
builder.AppendSwitch("--log-file", logFilePath.FullPath.Quote());
}

return builder;
}
Expand Down
Loading
Loading