Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ private FeatureFlag() { }
// Disable setting DOTNET_ROOT environment variable on non-Windows platforms. We used to set it only only on Windows when we found testhost.exe, now we set it always to allow xunit v3 to run tests in child process.
public const string VSTEST_DISABLE_DOTNET_ROOT_ON_NONWINDOWS = nameof(VSTEST_DISABLE_DOTNET_ROOT_ON_NONWINDOWS);

// Disable turning dynamic code coverage for native code to OFF by default. Setting this to 1 will skip adding the setting.
public const string VSTEST_DISABLE_DYNAMICNATIVE_CODECOVERAGE_DEFAULT_SETTING = nameof(VSTEST_DISABLE_DYNAMICNATIVE_CODECOVERAGE_DEFAULT_SETTING);



[Obsolete("Only use this in tests.")]
internal static void Reset()
Expand Down
81 changes: 81 additions & 0 deletions src/Microsoft.TestPlatform.Utilities/InferRunSettingsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -718,4 +718,85 @@ private static bool IsFrameworkIncompatible(Framework sourceFramework, Framework
return !sourceFramework.Name.Equals(Framework.DefaultFramework.Name, StringComparison.OrdinalIgnoreCase)
&& !sourceFramework.Name.Equals(targetFramework.Name, StringComparison.OrdinalIgnoreCase);
}

public static bool UpdateCollectCoverageSettings(XmlDocument xmlDocument)
{
var root = xmlDocument.DocumentElement;

// TODO: is this good way to find the node, can users have different casing, can they have uri="datacollector://Microsoft/CodeCoverage/2.0" or assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
var dataCollectorNodes = root?.SelectNodes("DataCollectionRunSettings/DataCollectors/DataCollector");
if (dataCollectorNodes == null)
{
return false;
}
foreach (XmlNode dataCollectorNode in dataCollectorNodes)
{
var dataCollectorFound = false;
foreach (XmlAttribute attribute in dataCollectorNode.Attributes!)
{
if (attribute.Name.Equals("friendlyName", StringComparison.OrdinalIgnoreCase) &&
attribute.Value.Equals("Code Coverage", StringComparison.OrdinalIgnoreCase))
{
dataCollectorFound = true;
break;
}

if (attribute.Name.Equals("uri", StringComparison.OrdinalIgnoreCase) &&
attribute.Value.Equals(CodeCoverageCollectorUri, StringComparison.OrdinalIgnoreCase))
{
dataCollectorFound = true;
break;
}

if (attribute.Name.Equals("assemblyQualifiedName", StringComparison.OrdinalIgnoreCase) &&
attribute.Value.IndexOf("Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector", StringComparison.OrdinalIgnoreCase) >= 0)
{
dataCollectorFound = true;
break;
}
}

if (dataCollectorFound)
{
var coverageCollectorNode = dataCollectorNode;
// Code coverage settings are present, we should update them.

var dynamicNativeInstrumentationNode = coverageCollectorNode.SelectSingleNode("Configuration/CodeCoverage/EnableDynamicNativeInstrumentation");

if (dynamicNativeInstrumentationNode == null)
{
// EnableDynamicNativeInstrumentation is not set explicitly, we should set it. Whole tree might not exist.

var currentNode = coverageCollectorNode;
var paths = "Configuration/CodeCoverage/EnableDynamicNativeInstrumentation".Split('/');
foreach (var nodeName in paths)
{
var found = false;
foreach (XmlNode childNode in currentNode.ChildNodes)
{
if (childNode.Name == nodeName)
{
currentNode = childNode;
found = true;
break;
}
}

if (!found)
{
var newNode = xmlDocument.CreateElement(nodeName);
currentNode.AppendChild(newNode);
currentNode = newNode;
}
}

currentNode.InnerXml = "False";

return true;
}
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ static Microsoft.VisualStudio.TestPlatform.Utilities.MSTestSettingsUtilities.IsL
static Microsoft.VisualStudio.TestPlatform.Utilities.ParallelRunSettingsUtilities.UpdateRunSettingsWithParallelSettingIfNotConfigured(System.Xml.XPath.XPathNavigator! navigator) -> void
static Microsoft.VisualStudio.TestPlatform.Utilities.StringExtensions.Tokenize(this string? input, char separator, char escape) -> System.Collections.Generic.IEnumerable<string!>!
static Microsoft.VisualStudio.TestPlatform.Utilities.InferRunSettingsHelper.UpdateBatchSize(System.Xml.XmlDocument! runSettingsDocument, long batchSizeValue) -> void
static Microsoft.VisualStudio.TestPlatform.Utilities.InferRunSettingsHelper.UpdateCollectCoverageSettings(System.Xml.XmlDocument! xmlDocument) -> bool


10 changes: 10 additions & 0 deletions src/vstest.console/TestPlatformHelpers/TestRequestManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,11 @@ private bool UpdateRunSettingsIfRequired(
settingsUpdated |= AddOrUpdateBuiltInLoggers(document, runConfiguration, loggerRunSettings);
settingsUpdated |= AddOrUpdateBatchSize(document, runConfiguration, isDiscovery);

if (!FeatureFlag.Instance.IsSet(FeatureFlag.VSTEST_DISABLE_DYNAMICNATIVE_CODECOVERAGE_DEFAULT_SETTING))
{
settingsUpdated |= UpdateCollectCoverageSettings(document, runConfiguration);
}

updatedRunSettingsXml = navigator.OuterXml;

return settingsUpdated;
Expand Down Expand Up @@ -1246,6 +1251,11 @@ private static bool UpdateMSBuildLoggerIfExists(
return false;
}

internal static bool UpdateCollectCoverageSettings(XmlDocument xmlDocument, RunConfiguration _)
{
return InferRunSettingsHelper.UpdateCollectCoverageSettings(xmlDocument);
}

private void RunTests(
IRequestData requestData,
TestRunCriteria testRunCriteria,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2663,6 +2663,147 @@ public void AddOrUpdateBatchSizeSetsRunConfigurationAndBatchSize()
Assert.AreEqual("<RunSettings><RunConfiguration><BatchSize>1000</BatchSize></RunConfiguration></RunSettings>", xmlDocument.OuterXml);
}

[TestMethod]
public void UpdateCodeCoverageSettings_SetEnableDynamicNativeInstrumentationToFalse_WhenNotPresent()
{
// Arrange
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml("""
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Configuration>
<CoverageLogLevel>All</CoverageLogLevel>
<InstrumentationLogLevel>All</InstrumentationLogLevel>
<ManagedVanguardLogLevel>Verbose</ManagedVanguardLogLevel>
<CoverageFileLogPath>%LOGS_DIR%</CoverageFileLogPath>
<CodeCoverage>
<FileLogPath>%LOGS_DIR%</FileLogPath>
<LogLevel>All</LogLevel>
<UseVerifiableInstrumentation>False</UseVerifiableInstrumentation>
<EnableStaticNativeInstrumentation>False</EnableStaticNativeInstrumentation>
</CodeCoverage>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>
""");
var configuration = new RunConfiguration();

// Act
var result = TestRequestManager.UpdateCollectCoverageSettings(xmlDocument, configuration);

// Assert
Assert.IsTrue(result);
StringAssert.Contains(xmlDocument.OuterXml, "<EnableStaticNativeInstrumentation>False</EnableStaticNativeInstrumentation><EnableDynamicNativeInstrumentation>False</EnableDynamicNativeInstrumentation></CodeCoverage>");
}

[TestMethod]
public void UpdateCodeCoverageSettings_SetEnableDynamicNativeInstrumentationToFalse_WhenNotPresentAndParentDetailsOfConfigurationAreAlsoNotPresent()
{
// Arrange
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml("""
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>
""");
var configuration = new RunConfiguration();

// Act
var result = TestRequestManager.UpdateCollectCoverageSettings(xmlDocument, configuration);

// Assert
Assert.IsTrue(result);
StringAssert.Contains(xmlDocument.OuterXml, $"<Configuration><CodeCoverage><EnableDynamicNativeInstrumentation>False</EnableDynamicNativeInstrumentation></CodeCoverage></Configuration></DataCollector>");
}

[TestMethod]
[DataRow("True")]
[DataRow("False")]
public void UpdateCodeCoverageSettings_DontSetEnableDynamicNativeInstrumentationToFalse_WhenAlreadyPresent(string setting)
{
// Arrange
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml($"""
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Configuration>
<CoverageLogLevel>All</CoverageLogLevel>
<InstrumentationLogLevel>All</InstrumentationLogLevel>
<ManagedVanguardLogLevel>Verbose</ManagedVanguardLogLevel>
<CoverageFileLogPath>%LOGS_DIR%</CoverageFileLogPath>
<CodeCoverage>
<EnableDynamicNativeInstrumentation>{setting}</EnableDynamicNativeInstrumentation>
</CodeCoverage>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>
""");
var configuration = new RunConfiguration();

// Act
var result = TestRequestManager.UpdateCollectCoverageSettings(xmlDocument, configuration);

// Assert
// No matter what user has set, we don't override it.
Assert.IsFalse(result);
StringAssert.Contains(xmlDocument.OuterXml, $"<CodeCoverage><EnableDynamicNativeInstrumentation>{setting}</EnableDynamicNativeInstrumentation></CodeCoverage>");
}

[TestMethod]
[DataRow("friendlyName=\"Code Coverage\"")]
[DataRow("friendlyName=\"code coverage\"")]
[DataRow("uri=\"datacollector://Microsoft/CodeCoverage/2.0\"")]
[DataRow("uri=\"datacollector://microsoft/codecoverage/2.0\"")]
[DataRow("assemblyQualifiedName=\"Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\"")]
public void UpdateCodeCoverageSettings_SetEnableDynamicNativeInstrumentationToFalse_WhenUserUsesImperfectNamesForCollector(string collector)
{
// Arrange
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml($"""
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector {collector}>
<Configuration>
<CoverageLogLevel>All</CoverageLogLevel>
<InstrumentationLogLevel>All</InstrumentationLogLevel>
<ManagedVanguardLogLevel>Verbose</ManagedVanguardLogLevel>
<CoverageFileLogPath>%LOGS_DIR%</CoverageFileLogPath>
<CodeCoverage>
</CodeCoverage>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>
""");
var configuration = new RunConfiguration();

// Act
var result = TestRequestManager.UpdateCollectCoverageSettings(xmlDocument, configuration);

// Assert
Assert.IsTrue(result);
StringAssert.Contains(xmlDocument.OuterXml, $"<CodeCoverage><EnableDynamicNativeInstrumentation>False</EnableDynamicNativeInstrumentation></CodeCoverage>");
}

private static DiscoveryRequestPayload CreateDiscoveryPayload(string runsettings)
{
var discoveryPayload = new DiscoveryRequestPayload
Expand Down