diff --git a/src/DataCollectors/TraceDataCollector/Microsoft.VisualStudio.TraceDataCollector.csproj b/src/DataCollectors/TraceDataCollector/Microsoft.VisualStudio.TraceDataCollector.csproj
index a1f23c7cae..b88a138708 100644
--- a/src/DataCollectors/TraceDataCollector/Microsoft.VisualStudio.TraceDataCollector.csproj
+++ b/src/DataCollectors/TraceDataCollector/Microsoft.VisualStudio.TraceDataCollector.csproj
@@ -15,6 +15,7 @@
+
diff --git a/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs b/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs
index bac2a03655..40a8a9a38f 100644
--- a/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs
+++ b/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs
@@ -11,6 +11,7 @@ namespace Microsoft.VisualStudio.Coverage
using System.Xml;
using Interfaces;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
+ using Microsoft.VisualStudio.TestPlatform.Utilities;
using TestPlatform.ObjectModel;
using TraceCollector;
using TraceCollector.Interfaces;
@@ -107,10 +108,29 @@ public virtual void Initialize(
IDataCollectionSink dataSink,
IDataCollectionLogger logger)
{
+ var defaultConfigurationElement = DynamicCoverageDataCollectorImpl.GetDefaultConfiguration();
+
+ try
+ {
+ var processor = new CodeCoverageRunSettingsProcessor(defaultConfigurationElement);
+ configurationElement = (XmlElement)processor.Process(configurationElement);
+ }
+ catch (Exception ex)
+ {
+ EqtTrace.Warning(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ string.Join(
+ " ",
+ "DynamicCoverageDataCollectorImpl.Initialize: Exception encountered while processing the configuration element.",
+ "Keeping the configuration element unaltered. More info about the exception: {0}"),
+ ex.Message));
+ }
+
EqtTrace.Info("DynamicCoverageDataCollectorImpl.Initialize: Initialize configuration. ");
if (string.IsNullOrEmpty(configurationElement?.InnerXml))
{
- configurationElement = DynamicCoverageDataCollectorImpl.GetDefaultConfiguration();
+ configurationElement = defaultConfigurationElement;
}
this.logger = logger;
diff --git a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs
new file mode 100644
index 0000000000..6e63832c43
--- /dev/null
+++ b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs
@@ -0,0 +1,318 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.VisualStudio.TestPlatform.Utilities
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Xml;
+ using System.Xml.XPath;
+
+ using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+
+ ///
+ /// Represents the run settings processor for code coverage data collectors.
+ ///
+ public class CodeCoverageRunSettingsProcessor
+ {
+ #region Members
+ ///
+ /// Represents the default settings loaded as an .
+ ///
+ private XmlNode defaultSettingsRootNode;
+ #endregion
+
+ #region Constructors & Helpers
+ ///
+ /// Constructs an object.
+ ///
+ ///
+ /// The default settings root node.
+ public CodeCoverageRunSettingsProcessor(XmlNode defaultSettingsRootNode)
+ {
+ if (defaultSettingsRootNode == null)
+ {
+ throw new ArgumentNullException("Default settings root node is null.");
+ }
+
+ this.defaultSettingsRootNode = defaultSettingsRootNode;
+ }
+ #endregion
+
+ #region Public Interface
+ ///
+ /// Processes the current settings for the code coverage data collector.
+ ///
+ ///
+ /// The code coverage settings.
+ ///
+ /// An updated version of the current run settings.
+ public XmlNode Process(string currentSettings)
+ {
+ if (string.IsNullOrEmpty(currentSettings))
+ {
+ return null;
+ }
+
+ // Load current settings from string.
+ var document = new XmlDocument();
+ document.LoadXml(currentSettings);
+
+ return this.Process(document.DocumentElement);
+ }
+
+ ///
+ /// Processes the current settings for the code coverage data collector.
+ ///
+ ///
+ ///
+ /// The code coverage settings document.
+ ///
+ ///
+ /// An updated version of the current run settings.
+ public XmlNode Process(XmlDocument currentSettingsDocument)
+ {
+ if (currentSettingsDocument == null)
+ {
+ return null;
+ }
+
+ return this.Process(currentSettingsDocument.DocumentElement);
+ }
+
+ ///
+ /// Processes the current settings for the code coverage data collector.
+ ///
+ ///
+ /// The code coverage root element.
+ ///
+ /// An updated version of the current run settings.
+ public XmlNode Process(XmlNode currentSettingsRootNode)
+ {
+ if (currentSettingsRootNode == null)
+ {
+ return null;
+ }
+
+ // Get the code coverage node from the current settings. If unable to get any
+ // particular component down the path just add the default values for that component
+ // from the default settings document and return since there's nothing else to be done.
+ var codeCoveragePathComponents = new List() { "CodeCoverage" };
+ var currentCodeCoverageNode = this.SelectNodeOrAddDefaults(
+ currentSettingsRootNode,
+ this.defaultSettingsRootNode,
+ codeCoveragePathComponents);
+
+ // Cannot extract current code coverage node from the given settings so we bail out.
+ // However, the default code coverage node has already been added to the document's
+ // root.
+ if (currentCodeCoverageNode == null)
+ {
+ return currentSettingsRootNode;
+ }
+
+ // Get the code coverage node from the default settings.
+ var defaultCodeCoverageNode = this.ExtractNode(
+ this.defaultSettingsRootNode,
+ this.BuildPath(codeCoveragePathComponents));
+
+ // Create the exclusion type list.
+ var exclusions = new List>
+ {
+ new List { "ModulePaths", "Exclude" },
+ new List { "Attributes", "Exclude" },
+ new List { "Sources", "Exclude" },
+ new List { "Functions", "Exclude" }
+ };
+
+ foreach (var exclusion in exclusions)
+ {
+ // Get the node for the current exclusion type. If unable to get any
+ // particular component down the path just add the default values for that
+ // component from the default settings document and continue since there's nothing
+ // else to be done.
+ var currentNode = this.SelectNodeOrAddDefaults(
+ currentCodeCoverageNode,
+ defaultCodeCoverageNode,
+ exclusion);
+
+ // Check if the node extraction was successful and we should process the current
+ // node in order to merge the current exclusion rules with the default ones.
+ if (currentNode == null)
+ {
+ continue;
+ }
+
+ // Extract the node from the default settings.
+ var defaultNode = this.ExtractNode(
+ defaultCodeCoverageNode,
+ this.BuildPath(exclusion));
+
+ // Merge the current and default settings for the current exclusion rule.
+ this.MergeNodes(currentNode, defaultNode);
+ }
+
+ return currentSettingsRootNode;
+ }
+ #endregion
+
+ #region Private Methods
+ ///
+ /// Selects the node from the current settings node using the given
+ /// style path. If unable to select the requested node it adds
+ /// default settings along the path.
+ ///
+ ///
+ ///
+ /// The root node from the current settings document for the extraction.
+ ///
+ ///
+ /// The corresponding root node from the default settings document.
+ ///
+ /// The path components.
+ ///
+ /// The requested node if successful, otherwise.
+ private XmlNode SelectNodeOrAddDefaults(
+ XmlNode currentRootNode,
+ XmlNode defaultRootNode,
+ IList pathComponents)
+ {
+ var currentNode = currentRootNode;
+ var partialPath = new StringBuilder();
+
+ partialPath.Append(".");
+
+ foreach (var component in pathComponents)
+ {
+ var currentPathComponent = "/" + component;
+
+ // Append the current path component to the partial path.
+ partialPath.Append(currentPathComponent);
+
+ // Extract the node corresponding to the latest path component.
+ var tempNode = this.ExtractNode(currentNode, "." + currentPathComponent);
+
+ // Extraction is pruned here because we shouldn't be processing the current node.
+ if (tempNode != null && !this.ShouldProcessCurrentExclusion(tempNode))
+ {
+ return null;
+ }
+
+ // If the current node extraction is unsuccessful then add the corresponding
+ // default settings node and bail out.
+ if (tempNode == null)
+ {
+ var defaultNode = this.ExtractNode(
+ defaultRootNode,
+ partialPath.ToString());
+
+ var importedChild = currentNode.OwnerDocument.ImportNode(defaultNode, true);
+ currentNode.AppendChild(importedChild);
+
+ return null;
+ }
+
+ // Node corresponding to the latest path component is the new root node for the
+ // next extraction.
+ currentNode = tempNode;
+ }
+
+ return currentNode;
+ }
+
+ ///
+ /// Checks if we should process the current exclusion node.
+ ///
+ ///
+ /// The current exclusion node.
+ ///
+ ///
+ /// if the node should be processed, otherwise.
+ ///
+ private bool ShouldProcessCurrentExclusion(XmlNode node)
+ {
+ const string attributeName = "mergeDefaults";
+
+ foreach (XmlAttribute attribute in node.Attributes)
+ {
+ // If the attribute is present and set on 'false' we skip processing for the
+ // current exclusion.
+ if (attribute.Name == attributeName
+ && bool.TryParse(attribute.Value, out var value)
+ && !value)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Assembles a relative path from the path given as components.
+ ///
+ ///
+ /// A relative path built from path components.
+ private string BuildPath(IList pathComponents)
+ {
+ return string.Join("/", new[] { "." }.Concat(pathComponents));
+ }
+
+ ///
+ /// Extracts the node specified by the current path using the provided node as root.
+ ///
+ ///
+ /// The root to be used for extraction.
+ /// The path used to specify the requested node.
+ ///
+ /// The extracted node if successful, otherwise.
+ private XmlNode ExtractNode(XmlNode node, string path)
+ {
+ try
+ {
+ return node.SelectSingleNode(path);
+ }
+ catch (XPathException ex)
+ {
+ EqtTrace.Error(
+ "CodeCoverageRunSettingsProcessor.ExtractNode: Cannot select single node \"{0}\".",
+ ex.Message);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Merges the current settings rules with the default settings rules.
+ ///
+ ///
+ /// The current settings root node.
+ /// The default settings root node.
+ private void MergeNodes(XmlNode currentNode, XmlNode defaultNode)
+ {
+ var exclusionCache = new HashSet();
+
+ // Add current exclusions to the exclusion cache.
+ foreach (XmlNode child in currentNode.ChildNodes)
+ {
+ exclusionCache.Add(child.OuterXml);
+ }
+
+ // Iterate through default exclusions and import missing ones.
+ foreach (XmlNode child in defaultNode.ChildNodes)
+ {
+ if (exclusionCache.Contains(child.OuterXml))
+ {
+ continue;
+ }
+
+ // Import missing default exclusions.
+ var importedChild = currentNode.OwnerDocument.ImportNode(child, true);
+ currentNode.AppendChild(importedChild);
+ }
+ }
+ #endregion
+ }
+}
diff --git a/test/DataCollectors/TraceDataCollector.UnitTests/DynamicCoverageDataCollectorImplTests.cs b/test/DataCollectors/TraceDataCollector.UnitTests/DynamicCoverageDataCollectorImplTests.cs
index 82d2ee58aa..65b3610833 100644
--- a/test/DataCollectors/TraceDataCollector.UnitTests/DynamicCoverageDataCollectorImplTests.cs
+++ b/test/DataCollectors/TraceDataCollector.UnitTests/DynamicCoverageDataCollectorImplTests.cs
@@ -4,6 +4,7 @@
namespace Microsoft.VisualStudio.TraceDataCollector.UnitTests
{
using System;
+ using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
@@ -85,9 +86,8 @@ public void InitializeShouldCreateDefaultCodeCoverageSettingsIfConfigElementIsNu
[TestMethod]
public void InitializeShouldInitializeVanguardWithRightCoverageSettings()
{
- var expectedContent = "CoverageSettingsContent";
XmlElement configElement =
- DynamicCoverageDataCollectorImplTests.CreateXmlElement($"{expectedContent}");
+ DynamicCoverageDataCollectorImplTests.CreateXmlElement(@"");
this.directoryHelperMock.Setup(d => d.CreateDirectory(It.IsAny()))
.Callback((path) =>
@@ -101,8 +101,20 @@ public void InitializeShouldInitializeVanguardWithRightCoverageSettings()
this.collectorImpl.Initialize(configElement, this.dataCollectionSinkMock.Object, this.dataCollectionLoggerMock.Object);
+ XmlDocument defaultDocument = new XmlDocument();
+ defaultDocument.LoadXml(DynamicCoverageDataCollectorImplTests.GetDefaultCodeCoverageConfig());
+
Assert.AreEqual(DynamicCoverageDataCollectorImplTests.DefaultConfigFileName, Path.GetFileName(this.aConfigFileName));
- Assert.AreEqual(expectedContent, File.ReadAllText(this.aConfigFileName));
+
+ XmlDocument currentDocument = new XmlDocument();
+ currentDocument.LoadXml(File.ReadAllText(this.aConfigFileName));
+
+ var codeCoverageNodes = new Tuple(currentDocument.DocumentElement, defaultDocument.DocumentElement);
+
+ this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./ModulePaths/Exclude");
+ this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./Functions/Exclude");
+ this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./Attributes/Exclude");
+ this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./Sources/Exclude");
}
[TestMethod]
@@ -374,6 +386,58 @@ private void CompareWithDefaultConfig()
File.ReadAllText(this.aConfigFileName).Replace(" ", string.Empty).Replace(Environment.NewLine, string.Empty));
}
+ private XmlNode ExtractNode(XmlNode node, string path)
+ {
+ try
+ {
+ return node.SelectSingleNode(path);
+ }
+ catch
+ {
+ }
+
+ return null;
+ }
+
+ private Tuple ExtractNodes(XmlNode currentSettingsRoot, XmlNode defaultSettingsRoot, string path)
+ {
+ var currentNode = this.ExtractNode(currentSettingsRoot, path);
+ var defaultNode = this.ExtractNode(defaultSettingsRoot, path);
+ Assert.IsNotNull(currentNode);
+ Assert.IsNotNull(defaultNode);
+
+ return new Tuple(currentNode, defaultNode);
+ }
+
+ private void CompareResults(XmlNode currentSettingsRoot, XmlNode defaultSettingsRoot, string path)
+ {
+ var nodes = this.ExtractNodes(currentSettingsRoot, defaultSettingsRoot, path);
+
+ Assert.AreEqual(nodes.Item1.ChildNodes.Count, nodes.Item2.ChildNodes.Count);
+
+ var set = new HashSet();
+ foreach (XmlNode child in nodes.Item1.ChildNodes)
+ {
+ if (!set.Contains(child.OuterXml))
+ {
+ set.Add(child.OuterXml);
+ }
+ }
+
+ foreach (XmlNode child in nodes.Item2.ChildNodes)
+ {
+ if (!set.Contains(child.OuterXml))
+ {
+ set.Add(child.OuterXml);
+ continue;
+ }
+
+ set.Remove(child.OuterXml);
+ }
+
+ Assert.AreEqual(set.Count, 0);
+ }
+
#endregion
}
}
diff --git a/test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageRunSettingsProcessorTests.cs b/test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageRunSettingsProcessorTests.cs
new file mode 100644
index 0000000000..7968ccee59
--- /dev/null
+++ b/test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageRunSettingsProcessorTests.cs
@@ -0,0 +1,284 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.TestPlatform.Utilities.UnitTests
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Reflection;
+ using System.Xml;
+ using Microsoft.VisualStudio.TestPlatform.Utilities;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class CodeCoverageRunSettingsProcessorTests
+ {
+ #region Members
+ private XmlElement defaultSettings;
+
+ private CodeCoverageRunSettingsProcessor processor;
+ #endregion
+
+ #region Constructors
+ public CodeCoverageRunSettingsProcessorTests()
+ {
+ this.defaultSettings = this.GetDefaultConfiguration();
+ this.processor = new CodeCoverageRunSettingsProcessor(this.defaultSettings);
+ }
+ #endregion
+
+ #region Test Methods
+ [TestMethod]
+ public void ProcessingShouldReturnNullForNullOrEmptySettings()
+ {
+ Assert.IsNull(processor.Process((string)null));
+ Assert.IsNull(processor.Process(""));
+
+ Assert.IsNull(processor.Process((XmlNode)null));
+
+ Assert.IsNull(processor.Process((XmlDocument)null));
+ }
+
+ [TestMethod]
+ public void MissingCodeCoverageTagShouldAddDefaultTag()
+ {
+ const string settings = "";
+ string expected = $"{this.defaultSettings.OuterXml}";
+
+ Assert.AreEqual(expected, processor.Process(settings).OuterXml);
+ }
+
+ [TestMethod]
+ public void EmptyCodeCoverageTagShouldAddDefaultTag()
+ {
+ const string settings = "";
+ var processedNode = processor.Process(settings);
+ Assert.IsNotNull(processedNode);
+
+ var codeCoverageNodes = this.ExtractNodes(processedNode, this.defaultSettings, "./CodeCoverage");
+
+ this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./ModulePaths/Exclude");
+ this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./Functions/Exclude");
+ this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./Attributes/Exclude");
+ this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./Sources/Exclude");
+ }
+
+ [TestMethod]
+ public void MergeDefaultsDisabledShouldReturnInputUnaltered()
+ {
+ var settings = string.Join(
+ Environment.NewLine,
+ @"",
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @" .*\\atlmfc\\.*",
+ @" .*\\atlmbfc\\.*",
+ @" .*\\vctools\\.*",
+ @" .*\\public\\sdk2\\.*",
+ @" .*\\externalapis\\.*",
+ @" .*\\microsoft sdks\\.*",
+ @" .*\\vc\\include\\.*",
+ @" .*\\msclr\\.*",
+ @" .*\\ucrt\\.*",
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @"");
+
+ var document = new XmlDocument();
+ document.LoadXml(settings);
+
+ Assert.AreEqual(document.OuterXml, processor.Process(settings).OuterXml);
+ }
+
+ [TestMethod]
+ public void MixedTestShouldCorrectlyAddMissingTags()
+ {
+ var settings = string.Join(
+ Environment.NewLine,
+ @"",
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @" .*\\atlmfc\\.*",
+ @" .*\\atlmbfc\\.*",
+ @" .*\\vctools\\.*",
+ @" .*\\public\\sdk2\\.*",
+ @" .*\\externalapis\\.*",
+ @" .*\\microsoft sdks\\.*",
+ @" .*\\vc\\include\\.*",
+ @" .*\\msclr\\.*",
+ @" .*\\ucrt\\.*",
+ @" ",
+ @" ",
+ @" ",
+ @"");
+
+ var expectedResult = string.Join(
+ Environment.NewLine,
+ @"",
+ @" ",
+ @" ",
+ @" ",
+ @" .*CPPUnitTestFramework.*",
+ @" .*vstest.console.*",
+ @" .*microsoft.intellitrace.*",
+ @" .*testhost.*",
+ @" .*datacollector.*",
+ @" .*microsoft.teamfoundation.testplatform.*",
+ @" .*microsoft.visualstudio.testplatform.*",
+ @" .*microsoft.visualstudio.testwindow.*",
+ @" .*microsoft.visualstudio.mstest.*",
+ @" .*microsoft.visualstudio.qualitytools.*",
+ @" .*microsoft.vssdk.testhostadapter.*",
+ @" .*microsoft.vssdk.testhostframework.*",
+ @" .*qtagent32.*",
+ @" .*msvcr.*dll$",
+ @" .*msvcp.*dll$",
+ @" .*clr.dll$",
+ @" .*clr.ni.dll$",
+ @" .*clrjit.dll$",
+ @" .*clrjit.ni.dll$",
+ @" .*mscoree.dll$",
+ @" .*mscoreei.dll$",
+ @" .*mscoreei.ni.dll$",
+ @" .*mscorlib.dll$",
+ @" .*mscorlib.ni.dll$",
+ @" .*cryptbase.dll$",
+ @" .*bcryptPrimitives.dll$",
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @" ^std::.*",
+ @" ^ATL::.*",
+ @" .*::__GetTestMethodInfo.*",
+ @" .*__CxxPureMSILEntry.*",
+ @" ^Microsoft::VisualStudio::CppCodeCoverageFramework::.*",
+ @" ^Microsoft::VisualStudio::CppUnitTestFramework::.*",
+ @" .*::YOU_CAN_ONLY_DESIGNATE_ONE_.*",
+ @" ^__.*",
+ @" .*::__.*",
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @" ^System.Diagnostics.DebuggerHiddenAttribute$",
+ @" ^System.Diagnostics.DebuggerNonUserCodeAttribute$",
+ @" System.Runtime.CompilerServices.CompilerGeneratedAttribute$",
+ @" ^System.CodeDom.Compiler.GeneratedCodeAttribute$",
+ @" ^System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute$",
+ @" ^Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode.*",
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @" .*\\atlmfc\\.*",
+ @" .*\\atlmbfc\\.*",
+ @" .*\\vctools\\.*",
+ @" .*\\public\\sdk2\\.*",
+ @" .*\\externalapis\\.*",
+ @" .*\\microsoft sdks\\.*",
+ @" .*\\vc\\include\\.*",
+ @" .*\\msclr\\.*",
+ @" .*\\ucrt\\.*",
+ @" .*\\public\\sdk\\.*",
+ @" ",
+ @" ",
+ @" ",
+ @"");
+
+ var expectedResultDocument = new XmlDocument();
+ expectedResultDocument.LoadXml(expectedResult);
+
+ var processedNode = processor.Process(settings);
+ Assert.IsNotNull(processedNode);
+
+ var codeCoverageNodes = this.ExtractNodes(processedNode, expectedResultDocument.DocumentElement, "./CodeCoverage");
+
+ this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./ModulePaths/Exclude");
+ this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./Functions/Exclude");
+ this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./Attributes/Exclude");
+ this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./Sources/Exclude");
+ }
+ #endregion
+
+ #region Helpers
+ private XmlNode ExtractNode(XmlNode node, string path)
+ {
+ try
+ {
+ return node.SelectSingleNode(path);
+ }
+ catch
+ {
+ }
+
+ return null;
+ }
+
+ private XmlElement GetDefaultConfiguration()
+ {
+ var document = new XmlDocument();
+ Assembly assembly = typeof(CodeCoverageRunSettingsProcessorTests).GetTypeInfo().Assembly;
+ using (Stream stream = assembly.GetManifestResourceStream(
+ "Microsoft.TestPlatform.Utilities.UnitTests.DefaultCodeCoverageConfig.xml"))
+ {
+ document.Load(stream);
+ }
+
+ return document.DocumentElement;
+ }
+
+ private Tuple ExtractNodes(XmlNode currentSettingsRoot, XmlNode defaultSettingsRoot, string path)
+ {
+ var currentNode = this.ExtractNode(currentSettingsRoot, path);
+ var defaultNode = this.ExtractNode(defaultSettingsRoot, path);
+ Assert.IsNotNull(currentNode);
+ Assert.IsNotNull(defaultNode);
+
+ return new Tuple(currentNode, defaultNode);
+ }
+
+ private void CompareResults(XmlNode currentSettingsRoot, XmlNode defaultSettingsRoot, string path)
+ {
+ var nodes = this.ExtractNodes(currentSettingsRoot, defaultSettingsRoot, path);
+
+ Assert.AreEqual(nodes.Item1.ChildNodes.Count, nodes.Item2.ChildNodes.Count);
+
+ var set = new HashSet();
+ foreach (XmlNode child in nodes.Item1.ChildNodes)
+ {
+ if (!set.Contains(child.OuterXml))
+ {
+ set.Add(child.OuterXml);
+ }
+ }
+
+ foreach (XmlNode child in nodes.Item2.ChildNodes)
+ {
+ if (!set.Contains(child.OuterXml))
+ {
+ set.Add(child.OuterXml);
+ continue;
+ }
+
+ set.Remove(child.OuterXml);
+ }
+
+ Assert.AreEqual(set.Count, 0);
+ }
+ #endregion
+ }
+}
diff --git a/test/Microsoft.TestPlatform.Utilities.UnitTests/DefaultCodeCoverageConfig.xml b/test/Microsoft.TestPlatform.Utilities.UnitTests/DefaultCodeCoverageConfig.xml
new file mode 100644
index 0000000000..3bc0b33877
--- /dev/null
+++ b/test/Microsoft.TestPlatform.Utilities.UnitTests/DefaultCodeCoverageConfig.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+ .*CPPUnitTestFramework.*
+ .*vstest.console.*
+ .*microsoft.intellitrace.*
+ .*testhost.*
+ .*datacollector.*
+ .*microsoft.teamfoundation.testplatform.*
+ .*microsoft.visualstudio.testplatform.*
+ .*microsoft.visualstudio.testwindow.*
+ .*microsoft.visualstudio.mstest.*
+ .*microsoft.visualstudio.qualitytools.*
+ .*microsoft.vssdk.testhostadapter.*
+ .*microsoft.vssdk.testhostframework.*
+ .*qtagent32.*
+ .*msvcr.*dll$
+ .*msvcp.*dll$
+ .*clr.dll$
+ .*clr.ni.dll$
+ .*clrjit.dll$
+ .*clrjit.ni.dll$
+ .*mscoree.dll$
+ .*mscoreei.dll$
+ .*mscoreei.ni.dll$
+ .*mscorlib.dll$
+ .*mscorlib.ni.dll$
+ .*cryptbase.dll$
+ .*bcryptPrimitives.dll$
+
+
+ True
+ True
+ True
+ false
+
+
+
+ ^std::.*
+ ^ATL::.*
+ .*::__GetTestMethodInfo.*
+ .*__CxxPureMSILEntry.*
+ ^Microsoft::VisualStudio::CppCodeCoverageFramework::.*
+ ^Microsoft::VisualStudio::CppUnitTestFramework::.*
+ .*::YOU_CAN_ONLY_DESIGNATE_ONE_.*
+ ^__.*
+ .*::__.*
+
+
+
+
+ ^System.Diagnostics.DebuggerHiddenAttribute$
+ ^System.Diagnostics.DebuggerNonUserCodeAttribute$
+ System.Runtime.CompilerServices.CompilerGeneratedAttribute$
+ ^System.CodeDom.Compiler.GeneratedCodeAttribute$
+ ^System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute$
+ ^Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode.*
+
+
+
+
+ .*\\atlmfc\\.*
+ .*\\vctools\\.*
+ .*\\public\\sdk\\.*
+ .*\\externalapis\\.*
+ .*\\microsoft sdks\\.*
+ .*\\vc\\include\\.*
+ .*\\msclr\\.*
+ .*\\ucrt\\.*
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.TestPlatform.Utilities.UnitTests/Microsoft.TestPlatform.Utilities.UnitTests.csproj b/test/Microsoft.TestPlatform.Utilities.UnitTests/Microsoft.TestPlatform.Utilities.UnitTests.csproj
index b123a6bb4c..d0c2957dff 100644
--- a/test/Microsoft.TestPlatform.Utilities.UnitTests/Microsoft.TestPlatform.Utilities.UnitTests.csproj
+++ b/test/Microsoft.TestPlatform.Utilities.UnitTests/Microsoft.TestPlatform.Utilities.UnitTests.csproj
@@ -24,5 +24,8 @@
+
+
+