Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding elements to code coverage config passed via commandline, #3162

Merged
10 commits merged into from
Nov 12, 2021
73 changes: 60 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,22 @@ 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);

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 +152,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 +167,38 @@ 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)
{
AddConfiguration(dataCollectorSettings.Configuration, keyValuePair[0], keyValuePair[1]);
}
else
{
throw new CommandLineException(exceptionMessage);
}
}
}

private static void AddConfiguration(XmlElement configuration, string configurationName, string configurationValue)
{
XmlElement attribute = configuration.OwnerDocument.CreateElement(configurationName);
attribute.InnerText = configurationValue;
configuration.AppendChild(attribute);
}

/// <summary>
Expand Down Expand Up @@ -209,9 +244,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)
{
EnabledDataCollectors.Add(argument.ToLower());
AddDataCollectorToRunSettings(new string[] { arguments }, runSettingsManager, fileHelper, string.Empty);
}

internal static void AddDataCollectorToRunSettings(string[] arguments, IRunSettingsProvider runSettingsManager, IFileHelper fileHelper, string exceptionMessage)
{
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 +270,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
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ namespace vstest.console.UnitTests.Processors
using System.IO;
using Microsoft.VisualStudio.TestPlatform.CommandLine;
using Microsoft.VisualStudio.TestPlatform.CommandLine.Processors;
using Microsoft.VisualStudio.TestPlatform.CommandLine.UnitTests;
using Microsoft.VisualStudio.TestPlatform.Common;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestPlatform.CommandLine.UnitTests;
using Moq;

using static Microsoft.VisualStudio.TestPlatform.CommandLine.Processors.CollectArgumentExecutor;

[TestClass]
Expand Down Expand Up @@ -531,7 +530,7 @@ public void InitializeXPlatCodeCoverageShouldAddXPlatInProcProcDataCollectoPrope
fileHelper.Setup(f => f.Exists(It.IsAny<string>())).Returns(true);
CollectArgumentExecutor executor = new CollectArgumentExecutor(settingsProvider, fileHelper.Object);
executor.Initialize("XPlat Code Coverage");

Assert.AreEqual(string.Join(Environment.NewLine,
"<?xml version=\"1.0\" encoding=\"utf-16\"?>",
"<RunSettings>",
Expand Down Expand Up @@ -586,6 +585,80 @@ public void InitializeXPlatCodeCoverageShouldAddXPlatInProcProcDataCollectoPrope
"</RunSettings>"), this.settingsProvider.ActiveRunSettings.SettingsXml);
}

[TestMethod]
public void InitializeShouldThrowExceptionWhenInvalidConfigurationsProvided()
{
var runsettingsString = string.Format(DefaultRunSettings, "");
var runsettings = new RunSettings();
runsettings.LoadSettingsXml(runsettingsString);
this.settingsProvider.SetActiveRunSettings(runsettings);

Assert.ThrowsException<CommandLineException>(() => this.executor.Initialize("MyDataCollector;SomeSetting"));
}

[TestMethod]
public void InitializeShouldCreateConfigurationsForNewDataCollectorInRunSettings()
{
var runsettingsString = string.Format(DefaultRunSettings, "");
var runsettings = new RunSettings();
runsettings.LoadSettingsXml(runsettingsString);
this.settingsProvider.SetActiveRunSettings(runsettings);

this.executor.Initialize("MyDataCollector;SomeSetting=SomeValue;AnotherSetting=AnotherValue");

Assert.IsNotNull(this.settingsProvider.ActiveRunSettings);
Assert.AreEqual(string.Join(Environment.NewLine,
"<?xml version=\"1.0\" encoding=\"utf-16\"?>",
"<RunSettings>",
" <RunConfiguration>",
" <TestAdaptersPaths>c:\\AdapterFolderPath</TestAdaptersPaths>",
" </RunConfiguration>",
" <DataCollectionRunSettings>",
" <DataCollectors>",
" <DataCollector friendlyName=\"MyDataCollector\" enabled=\"True\">",
" <Configuration>",
" <SomeSetting>SomeValue</SomeSetting>",
" <AnotherSetting>AnotherValue</AnotherSetting>",
" </Configuration>",
" </DataCollector>",
" </DataCollectors>",
" </DataCollectionRunSettings>",
"</RunSettings>"), this.settingsProvider.ActiveRunSettings.SettingsXml);
}

[TestMethod]
public void InitializeShouldCreateConfigurationsForExistingDataCollectorInRunSettings()
{
var runsettingsString = string.Format(DefaultRunSettings,
"<DataCollector friendlyName=\"MyDataCollector\" enabled=\"False\">" +
" <Configuration>" +
" <SomeSetting>SomeValue</SomeSetting>" +
" </Configuration>" +
"</DataCollector>");
var runsettings = new RunSettings();
runsettings.LoadSettingsXml(runsettingsString);
this.settingsProvider.SetActiveRunSettings(runsettings);

this.executor.Initialize("MyDataCollector;AnotherSetting=AnotherValue");

Assert.AreEqual(string.Join(Environment.NewLine,
"<?xml version=\"1.0\" encoding=\"utf-16\"?>",
"<RunSettings>",
" <RunConfiguration>",
" <TestAdaptersPaths>c:\\AdapterFolderPath</TestAdaptersPaths>",
" </RunConfiguration>",
" <DataCollectionRunSettings>",
" <DataCollectors>",
" <DataCollector friendlyName=\"MyDataCollector\" enabled=\"True\">",
" <Configuration>",
" <SomeSetting>SomeValue</SomeSetting>",
jakubch1 marked this conversation as resolved.
Show resolved Hide resolved
" <AnotherSetting>AnotherValue</AnotherSetting>",
" </Configuration>",
" </DataCollector>",
" </DataCollectors>",
" </DataCollectionRunSettings>",
"</RunSettings>"), this.settingsProvider.ActiveRunSettings.SettingsXml);
jakubch1 marked this conversation as resolved.
Show resolved Hide resolved
}
#endregion
}
}