Skip to content
Merged
4 changes: 2 additions & 2 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Dependencies>
<ProductDependencies>
<Dependency Name="Microsoft.Internal.CodeCoverage" Version="17.1.0-beta.21560.1">
<Dependency Name="Microsoft.Internal.CodeCoverage" Version="17.1.0-beta.21561.1">
<Uri>https://dev.azure.com/devdiv/DevDiv/_git/vs-code-coverage</Uri>
<Sha>778edda8bff7e4ba06942b971c3e9618bee2a0a5</Sha>
<Sha>3d834400e3b8cfcb3e8d97a51c7fb0b7fb4c02d9</Sha>
</Dependency>
<Dependency Name="Microsoft.Diagnostics.NETCore.Client" Version="0.2.0-preview.21508.1">
<Uri>https://github.com/dotnet/diagnostics</Uri>
Expand Down
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
JS etc.) is risky. This can break setup authoring and cause runtime failures in workloads
where Rolsyn is not installed. -->
<MicrosoftCodeAnalysisVersion>3.8.0-3.20427.2</MicrosoftCodeAnalysisVersion>
<MicrosoftInternalCodeCoverageVersion>17.1.0-beta.21560.1</MicrosoftInternalCodeCoverageVersion>
<MicrosoftInternalCodeCoverageVersion>17.1.0-beta.21561.1</MicrosoftInternalCodeCoverageVersion>
<MicrosoftDiagnosticsNETCoreClientVersion>0.2.0-preview.21508.1</MicrosoftDiagnosticsNETCoreClientVersion>
<MicrosoftSourceBuildIntermediatediagnosticsVersion>5.0.0-preview.21508.1</MicrosoftSourceBuildIntermediatediagnosticsVersion>
</PropertyGroup>
Expand Down
91 changes: 78 additions & 13 deletions src/vstest.console/Processors/CollectArgumentProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.Processors
{
using System;
using System.Collections.Generic;

using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
using Microsoft.VisualStudio.TestPlatform.CommandLine.Processors.Utilities;
using Microsoft.VisualStudio.TestPlatform.Common;
using Microsoft.VisualStudio.TestPlatform.Common.Interfaces;
using Microsoft.VisualStudio.TestPlatform.Common.Utilities;
Expand Down Expand Up @@ -108,21 +110,28 @@ public void Initialize(string argument)
// 1. Disable all other data collectors. Enable only those data collectors that are explicitly specified by user.
// 2. Check if Code Coverage Data Collector is specified in runsettings, if not add it and also set enable to true.

string exceptionMessage = string.Format(CultureInfo.CurrentUICulture, CommandLineResources.DataCollectorFriendlyNameInvalid, argument);

// if argument is null or doesn't contain any element, don't do anything.
if (string.IsNullOrWhiteSpace(argument))
{
throw new CommandLineException(
string.Format(
CultureInfo.CurrentUICulture,
CommandLineResources.DataCollectorFriendlyNameInvalid,
argument));
throw new CommandLineException(exceptionMessage);
}

// Get collect argument list.
var collectArgumentList = ArgumentProcessorUtilities.GetArgumentList(argument, ArgumentProcessorUtilities.SemiColonArgumentSeparator, exceptionMessage);

// First argument is collector name. Remaining are key value pairs for configurations.
if (collectArgumentList[0].Contains("="))
{
throw new CommandLineException(exceptionMessage);
}

if (InferRunSettingsHelper.IsTestSettingsEnabled(this.runSettingsManager.ActiveRunSettings.SettingsXml))
{
throw new SettingsException(string.Format(CommandLineResources.CollectWithTestSettingErrorMessage, argument));
}
AddDataCollectorToRunSettings(argument, this.runSettingsManager, this.fileHelper);
AddDataCollectorToRunSettings(collectArgumentList, this.runSettingsManager, this.fileHelper, exceptionMessage);
}

/// <summary>
Expand All @@ -149,7 +158,7 @@ public ArgumentProcessorResult Execute()
return ArgumentProcessorResult.Success;
}

internal static void EnableDataCollectorUsingFriendlyName(string argument, DataCollectionRunSettings dataCollectionRunSettings)
internal static DataCollectorSettings EnableDataCollectorUsingFriendlyName(string argument, DataCollectionRunSettings dataCollectionRunSettings)
{
DataCollectorSettings dataCollectorSettings = null;

Expand All @@ -164,6 +173,50 @@ internal static void EnableDataCollectorUsingFriendlyName(string argument, DataC
{
dataCollectorSettings.IsEnabled = true;
}

return dataCollectorSettings;
}

private static void AddDataCollectorConfigurations(string[] configurations, DataCollectorSettings dataCollectorSettings, string exceptionMessage)
{
if (dataCollectorSettings.Configuration == null)
{
XmlDocument doc = new XmlDocument();
dataCollectorSettings.Configuration = doc.CreateElement("Configuration");
}

foreach (var configuration in configurations)
{
var keyValuePair = ArgumentProcessorUtilities.GetArgumentList(configuration, ArgumentProcessorUtilities.EqualNameValueSeparator, exceptionMessage);

if (keyValuePair.Length == 2)
{
AddOrUpdateConfiguration(dataCollectorSettings.Configuration, keyValuePair[0], keyValuePair[1]);
}
else
{
throw new CommandLineException(exceptionMessage);
}
}
}

private static void AddOrUpdateConfiguration(XmlElement configuration, string configurationName, string configurationValue)
{
var existingConfigurations = configuration.GetElementsByTagName(configurationName);

// Update existing configuration if present.
if (existingConfigurations.Count == 0)
{
XmlElement newConfiguration = configuration.OwnerDocument.CreateElement(configurationName);
newConfiguration.InnerText = configurationValue;
configuration.AppendChild(newConfiguration);
return;
}

foreach (XmlNode existingConfiguration in existingConfigurations)
{
existingConfiguration.InnerText = configurationValue;
}
}

/// <summary>
Expand Down Expand Up @@ -209,9 +262,16 @@ private static bool DoesDataCollectorSettingsExist(string friendlyName,
return false;
}

internal static void AddDataCollectorToRunSettings(string argument, IRunSettingsProvider runSettingsManager, IFileHelper fileHelper)
internal static void AddDataCollectorToRunSettings(string arguments, IRunSettingsProvider runSettingsManager, IFileHelper fileHelper)
{
AddDataCollectorToRunSettings(new string[] { arguments }, runSettingsManager, fileHelper, string.Empty);
}

internal static void AddDataCollectorToRunSettings(string[] arguments, IRunSettingsProvider runSettingsManager, IFileHelper fileHelper, string exceptionMessage)
{
EnabledDataCollectors.Add(argument.ToLower());
var collectorName = arguments[0];
var additionalConfigurations = arguments.Skip(1).ToArray();
EnabledDataCollectors.Add(collectorName.ToLower());

var settings = runSettingsManager.ActiveRunSettings?.SettingsXml;
if (settings == null)
Expand All @@ -228,14 +288,19 @@ internal static void AddDataCollectorToRunSettings(string argument, IRunSettings
Constants.InProcDataCollectorSettingName);

// Add data collectors if not already present, enable if already present.
EnableDataCollectorUsingFriendlyName(argument, dataCollectionRunSettings);
var dataCollectorSettings = EnableDataCollectorUsingFriendlyName(collectorName, dataCollectionRunSettings);

if (additionalConfigurations.Length > 0)
{
AddDataCollectorConfigurations(additionalConfigurations, dataCollectorSettings, exceptionMessage);
}

runSettingsManager.UpdateRunSettingsNodeInnerXml(Constants.DataCollectionRunSettingsName, dataCollectionRunSettings.ToXml().InnerXml);

if (string.Equals(argument, CoverletConstants.CoverletDataCollectorFriendlyName, StringComparison.OrdinalIgnoreCase))
if (string.Equals(collectorName, CoverletConstants.CoverletDataCollectorFriendlyName, StringComparison.OrdinalIgnoreCase))
{
// Add in-proc data collector to runsettings if coverlet code coverage is enabled
EnableCoverletInProcDataCollector(argument, inProcDataCollectionRunSettings, runSettingsManager, fileHelper);
EnableCoverletInProcDataCollector(collectorName, inProcDataCollectionRunSettings, runSettingsManager, fileHelper);
runSettingsManager.UpdateRunSettingsNodeInnerXml(Constants.InProcDataCollectionRunSettingsName, inProcDataCollectionRunSettings.ToXml().InnerXml);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ internal class EnableCodeCoverageArgumentExecutor : IArgumentExecutor
private CommandLineOptions commandLineOptions;
private IFileHelper fileHelper;

private const string FriendlyName = "Code Coverage";
internal const string FriendlyName = "Code Coverage";

private static string xPathSeperator = "/";
private static string[] nodeNames = new string[] { Constants.RunSettingsName, Constants.DataCollectionRunSettingsName, Constants.DataCollectorsSettingName, Constants.DataCollectorSettingName };
Expand Down
66 changes: 65 additions & 1 deletion test/Microsoft.TestPlatform.AcceptanceTests/CodeCoverageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ public enum SettingsType
{
None = 0,
Default = 1,
Custom = 2
Custom = 2,
XmlOutput = 3,
CoberturaOutput = 4
}

public string AssemblyName { get; set; }
Expand Down Expand Up @@ -168,6 +170,44 @@ public void CodeCoverageShouldAvoidExclusionsX64(RunnerInfo runnerInfo)
this.CollectCodeCoverage(runnerInfo, parameters);
}

[TestMethod]
[NetFullTargetFrameworkDataSource(useDesktopRunner: false)]
[NetCoreTargetFrameworkDataSource(useDesktopRunner: false)]
public void CollectCodeCoverageSpecifyOutputFormatXml(RunnerInfo runnerInfo)
{
var parameters = new TestParameters()
{
AssemblyName = "SimpleTestProject.dll",
TargetPlatform = "x64",
RunSettingsPath = string.Empty,
RunSettingsType = TestParameters.SettingsType.XmlOutput,
ExpectedPassedTests = 1,
ExpectedSkippedTests = 1,
ExpectedFailedTests = 1
};

this.CollectCodeCoverage(runnerInfo, parameters);
}

[TestMethod]
[NetFullTargetFrameworkDataSource(useDesktopRunner: false)]
[NetCoreTargetFrameworkDataSource(useDesktopRunner: false)]
public void CollectCodeCoverageSpecifyOutputFormatCobertura(RunnerInfo runnerInfo)
{
var parameters = new TestParameters()
{
AssemblyName = "SimpleTestProject.dll",
TargetPlatform = "x64",
RunSettingsPath = string.Empty,
RunSettingsType = TestParameters.SettingsType.CoberturaOutput,
ExpectedPassedTests = 1,
ExpectedSkippedTests = 1,
ExpectedFailedTests = 1
};

this.CollectCodeCoverage(runnerInfo, parameters);
}

private void CollectCodeCoverage(RunnerInfo runnerInfo, TestParameters testParameters)
{
AcceptanceTestBase.SetTestEnvironment(this.testEnvironment, runnerInfo);
Expand All @@ -185,6 +225,19 @@ private void CollectCodeCoverage(RunnerInfo runnerInfo, TestParameters testParam
Console.WriteLine($@"Coverage file: {actualCoverageFile} Results directory: {resultsDirectory} trxfile: {trxFilePath}");
Assert.IsTrue(File.Exists(actualCoverageFile), "Coverage file not found: {0}", actualCoverageFile);

if (testParameters.RunSettingsType == TestParameters.SettingsType.XmlOutput)
{
Assert.IsTrue(actualCoverageFile.EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase));
}
else if (testParameters.RunSettingsType == TestParameters.SettingsType.CoberturaOutput)
{
Assert.IsTrue(actualCoverageFile.EndsWith(".cobertura.xml", StringComparison.InvariantCultureIgnoreCase));
}
else
{
Assert.IsTrue(actualCoverageFile.EndsWith(".coverage", StringComparison.InvariantCultureIgnoreCase));
}

var coverageDocument = this.GetXmlCoverage(actualCoverageFile);
if (testParameters.CheckSkipped)
{
Expand Down Expand Up @@ -232,6 +285,12 @@ private string CreateArguments(
case TestParameters.SettingsType.Custom:
runSettings = $" /settings:{testParameters.RunSettingsPath}";
break;
case TestParameters.SettingsType.XmlOutput:
runSettings = $" /collect:\"Code Coverage;CovrerageFileFormat=Xml\"";
break;
case TestParameters.SettingsType.CoberturaOutput:
Comment thread
jakubch1 marked this conversation as resolved.
runSettings = $" /collect:\"Code Coverage;CovrerageFileFormat=Cobertura\"";
break;
}

arguments = string.Concat(arguments, runSettings);
Expand Down Expand Up @@ -262,6 +321,11 @@ private void AssertSkippedMethod(XmlDocument document)
private void ValidateCoverageData(XmlDocument document, string moduleName)
{
var module = this.GetModuleNode(document.DocumentElement, moduleName.ToLower());

if (module == null)
{
module = this.GetModuleNode(document.DocumentElement, moduleName);
}
Assert.IsNotNull(module);

this.AssertCoverage(module, CodeCoverageAcceptanceTestBase.ExpectedMinimalModuleCoverage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ private string GetCodeCoverageRunSettings(int cpuCount, bool useClrIeInstrumenta
<Configuration>
<CLRIEInstrumentationNetCore>{useClrIeInstrumentationEngine}</CLRIEInstrumentationNetCore>
<CLRIEInstrumentationNetFramework>{useClrIeInstrumentationEngine}</CLRIEInstrumentationNetFramework>
<CoverageFileOutputFormats>{outputFormat}</CoverageFileOutputFormats>
<OutputFormat>{outputFormat}</OutputFormat>
Comment thread
fhnaseer marked this conversation as resolved.
Outdated
<CodeCoverage>
<ModulePaths>
<Exclude>
Expand Down
Loading