From 752244af505123ef220a40b9a0f853c2970a3356 Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Tue, 5 May 2020 18:12:04 +0200 Subject: [PATCH 01/15] Initial support for default exclusion merging --- .../CodeCoverageRunSettingsProcessor.cs | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs diff --git a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs new file mode 100644 index 0000000000..f02d480fda --- /dev/null +++ b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs @@ -0,0 +1,196 @@ +// 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 Microsoft.VisualStudio.TestPlatform.ObjectModel; + using System; + using System.Collections.Generic; + using System.Xml; + + internal class ExclusionType + { + private string path; + + private IDictionary exclusionRules; + + public ExclusionType(string path) + { + this.path = path; + + this.exclusionRules = new Dictionary(); + } + + public void AddExclusionNodes(XmlDocument doc) + { + var masterNode = doc.SelectSingleNode(this.path); + + foreach (XmlNode child in masterNode.ChildNodes) + { + var key = child.OuterXml; + if (this.exclusionRules.ContainsKey(key)) + { + continue; + } + + this.exclusionRules.Add(key, child); + } + } + + public void ReplaceExclusionNodes(XmlDocument doc) + { + var masterNode = doc.SelectSingleNode(this.path); + + masterNode.RemoveAll(); + foreach (var child in this.exclusionRules.Values) + { + masterNode.AppendChild(child); + } + } + } + + internal class CodeCoverageRunSettingsProcessor + { + private static readonly string CodeCoverageCollectorDefaultSettings = + @"" + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" .*CPPUnitTestFramework.*" + Environment.NewLine + + @" .*vstest.console.*" + Environment.NewLine + + @" .*microsoft.intellitrace.*" + Environment.NewLine + + @" .*testhost.*" + Environment.NewLine + + @" .*datacollector.*" + Environment.NewLine + + @" .*microsoft.teamfoundation.testplatform.*" + Environment.NewLine + + @" .*microsoft.visualstudio.testplatform.*" + Environment.NewLine + + @" .*microsoft.visualstudio.testwindow.*" + Environment.NewLine + + @" .*microsoft.visualstudio.mstest.*" + Environment.NewLine + + @" .*microsoft.visualstudio.qualitytools.*" + Environment.NewLine + + @" .*microsoft.vssdk.testhostadapter.*" + Environment.NewLine + + @" .*microsoft.vssdk.testhostframework.*" + Environment.NewLine + + @" .*qtagent32.*" + Environment.NewLine + + @" .*msvcr.*dll$" + Environment.NewLine + + @" .*msvcp.*dll$" + Environment.NewLine + + @" .*clr.dll$" + Environment.NewLine + + @" .*clr.ni.dll$" + Environment.NewLine + + @" .*clrjit.dll$" + Environment.NewLine + + @" .*clrjit.ni.dll$" + Environment.NewLine + + @" .*mscoree.dll$" + Environment.NewLine + + @" .*mscoreei.dll$" + Environment.NewLine + + @" .*mscoreei.ni.dll$" + Environment.NewLine + + @" .*mscorlib.dll$" + Environment.NewLine + + @" .*mscorlib.ni.dll$" + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" True" + Environment.NewLine + + @" True" + Environment.NewLine + + @" True" + Environment.NewLine + + @" false" + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" ^std::.*" + Environment.NewLine + + @" ^ATL::.*" + Environment.NewLine + + @" .*::__GetTestMethodInfo.*" + Environment.NewLine + + @" .*__CxxPureMSILEntry.*" + Environment.NewLine + + @" ^Microsoft::VisualStudio::CppCodeCoverageFramework::.*" + Environment.NewLine + + @" ^Microsoft::VisualStudio::CppUnitTestFramework::.*" + Environment.NewLine + + @" .*::YOU_CAN_ONLY_DESIGNATE_ONE_.*" + Environment.NewLine + + @" ^__.*" + Environment.NewLine + + @" .*::__.*" + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" ^System.Diagnostics.DebuggerHiddenAttribute$" + Environment.NewLine + + @" ^System.Diagnostics.DebuggerNonUserCodeAttribute$" + Environment.NewLine + + @" ^System.Runtime.CompilerServices.CompilerGeneratedAttribute$" + Environment.NewLine + + @" ^System.CodeDom.Compiler.GeneratedCodeAttribute$" + Environment.NewLine + + @" ^System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute$" + Environment.NewLine + + @" ^Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode.*" + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" .*\\atlmfc\\.*" + Environment.NewLine + + @" .*\\vctools\\.*" + Environment.NewLine + + @" .*\\public\\sdk\\.*" + Environment.NewLine + + @" .*\\externalapis\\.*" + Environment.NewLine + + @" .*\\microsoft sdks\\.*" + Environment.NewLine + + @" .*\\vc\\include\\.*" + Environment.NewLine + + @" .*\\msclr\\.*" + Environment.NewLine + + @" .*\\ucrt\\.*" + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @" " + Environment.NewLine + + @""; + + private XmlDocument defaultRunSettingsDocument; + + private XmlDocument runSettingsDocument; + + private IEnumerable exclusions; + + public CodeCoverageRunSettingsProcessor(string runSettings) + { + ValidateArg.NotNullOrEmpty(runSettings, nameof(runSettings)); + + runSettingsDocument = new XmlDocument(); + runSettingsDocument.LoadXml(runSettings); + + this.Initialize(runSettingsDocument); + } + + public CodeCoverageRunSettingsProcessor(XmlDocument runSettingsDocument) + { + ValidateArg.NotNull(runSettingsDocument, nameof(runSettingsDocument)); + + this.Initialize(runSettingsDocument); + } + + private void Initialize(XmlDocument runSettingsDocument) + { + this.runSettingsDocument = runSettingsDocument; + + defaultRunSettingsDocument = new XmlDocument(); + defaultRunSettingsDocument.LoadXml(CodeCoverageRunSettingsProcessor.CodeCoverageCollectorDefaultSettings); + + this.exclusions = new List() + { + new ExclusionType(@"/DataCollector/Configuration/CodeCoverage/ModulePaths/Exclude"), + new ExclusionType(@"/DataCollector/Configuration/CodeCoverage/Functions/Exclude"), + new ExclusionType(@"/DataCollector/Configuration/CodeCoverage/Attributes/Exclude"), + new ExclusionType(@"/DataCollector/Configuration/CodeCoverage/Sources/Exclude") + }; + } + + public string Process() + { + this.Merge(); + this.Replace(); + + return this.runSettingsDocument.OuterXml; + } + + private void Merge() + { + foreach (var exclusionType in this.exclusions) + { + exclusionType.AddExclusionNodes(this.defaultRunSettingsDocument); + exclusionType.AddExclusionNodes(this.runSettingsDocument); + } + } + + private void Replace() + { + foreach (var exclusionType in this.exclusions) + { + exclusionType.ReplaceExclusionNodes(this.runSettingsDocument); + } + } + } +} From 25a8e84b90155083575be83b24ac4f1391b775e3 Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Mon, 11 May 2020 19:49:55 +0200 Subject: [PATCH 02/15] Refined default exclusion merging --- .../DataCollection/DataCollectionManager.cs | 3 + .../CodeCoverageRunSettingsProcessor.cs | 191 ++++++++++++------ 2 files changed, 136 insertions(+), 58 deletions(-) diff --git a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs index dbebbe63f9..9fe6e31abd 100644 --- a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs +++ b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs @@ -13,6 +13,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollector using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework; using Microsoft.VisualStudio.TestPlatform.Common.Logging; using Microsoft.VisualStudio.TestPlatform.Common.Utilities; + using Microsoft.VisualStudio.TestPlatform.Utilities; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; @@ -153,6 +154,8 @@ public IDictionary InitializeDataCollectors(string settingsXml) ValidateArg.NotNull(settingsXml, "settingsXml"); + settingsXml = (new CodeCoverageRunSettingsProcessor(settingsXml)).Process(); + var sessionId = new SessionId(Guid.NewGuid()); var dataCollectionContext = new DataCollectionContext(sessionId); this.dataCollectionEnvironmentContext = DataCollectionEnvironmentContext.CreateForLocalEnvironment(dataCollectionContext); diff --git a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs index f02d480fda..a2a699204a 100644 --- a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs +++ b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs @@ -7,56 +7,17 @@ namespace Microsoft.VisualStudio.TestPlatform.Utilities using System; using System.Collections.Generic; using System.Xml; + using System.Xml.XPath; - internal class ExclusionType - { - private string path; - - private IDictionary exclusionRules; - - public ExclusionType(string path) - { - this.path = path; - - this.exclusionRules = new Dictionary(); - } - - public void AddExclusionNodes(XmlDocument doc) - { - var masterNode = doc.SelectSingleNode(this.path); - - foreach (XmlNode child in masterNode.ChildNodes) - { - var key = child.OuterXml; - if (this.exclusionRules.ContainsKey(key)) - { - continue; - } - - this.exclusionRules.Add(key, child); - } - } - - public void ReplaceExclusionNodes(XmlDocument doc) - { - var masterNode = doc.SelectSingleNode(this.path); - - masterNode.RemoveAll(); - foreach (var child in this.exclusionRules.Values) - { - masterNode.AppendChild(child); - } - } - } - - internal class CodeCoverageRunSettingsProcessor + public class CodeCoverageRunSettingsProcessor { + #region Members private static readonly string CodeCoverageCollectorDefaultSettings = @"" + Environment.NewLine + @" " + Environment.NewLine + @" " + Environment.NewLine + @" " + Environment.NewLine + - @" " + Environment.NewLine + + @" " + Environment.NewLine + @" .*CPPUnitTestFramework.*" + Environment.NewLine + @" .*vstest.console.*" + Environment.NewLine + @" .*microsoft.intellitrace.*" + Environment.NewLine + @@ -133,8 +94,10 @@ internal class CodeCoverageRunSettingsProcessor private XmlDocument runSettingsDocument; - private IEnumerable exclusions; + private IEnumerable>> exclusionClasses; + #endregion + #region Constructors & Helpers public CodeCoverageRunSettingsProcessor(string runSettings) { ValidateArg.NotNullOrEmpty(runSettings, nameof(runSettings)); @@ -159,38 +122,150 @@ private void Initialize(XmlDocument runSettingsDocument) defaultRunSettingsDocument = new XmlDocument(); defaultRunSettingsDocument.LoadXml(CodeCoverageRunSettingsProcessor.CodeCoverageCollectorDefaultSettings); - this.exclusions = new List() + this.exclusionClasses = new List>>() { - new ExclusionType(@"/DataCollector/Configuration/CodeCoverage/ModulePaths/Exclude"), - new ExclusionType(@"/DataCollector/Configuration/CodeCoverage/Functions/Exclude"), - new ExclusionType(@"/DataCollector/Configuration/CodeCoverage/Attributes/Exclude"), - new ExclusionType(@"/DataCollector/Configuration/CodeCoverage/Sources/Exclude") + new Tuple>( + @"./Configuration/CodeCoverage/ModulePaths/Exclude", + new Dictionary()), + + new Tuple>( + @"./Configuration/CodeCoverage/Attributes/Exclude", + new Dictionary()), + + new Tuple>( + @"./Configuration/CodeCoverage/Sources/Exclude", + new Dictionary()), + + new Tuple>( + @"./Configuration/CodeCoverage/Functions/Exclude", + new Dictionary()) }; } + #endregion + #region Public Interface public string Process() { - this.Merge(); - this.Replace(); + var codeCoverageDataCollectorNode = GetCodeCoverageDataCollectorNode(); + if (codeCoverageDataCollectorNode == null) + { + // What do we do if we cannot extract code coverage data collectors node ? + return this.runSettingsDocument.OuterXml; + } + + foreach (var exclusionClass in this.exclusionClasses) + { + var node = this.ExtractNode(codeCoverageDataCollectorNode, exclusionClass.Item1); + if (node == null) + { + // What do we do if we cannot extract the specified class ? + continue; + } + + if (!this.ShouldProcessCurrentExclusionClass(node)) + { + continue; + } + + var defaultNode = this.ExtractNode(this.defaultRunSettingsDocument.FirstChild, exclusionClass.Item1); + + AddNodes(defaultNode, exclusionClass); + AddNodes(node, exclusionClass); + + ReplaceNodes(node, exclusionClass); + } return this.runSettingsDocument.OuterXml; } + #endregion + + #region Private Methods + private XmlNode GetCodeCoverageDataCollectorNode() + { + const string prefix = @"/RunSettings/DataCollectionRunSettings/DataCollectors"; + + var dataCollectorsNode = this.ExtractNode(this.runSettingsDocument, prefix); + if (dataCollectorsNode == null) + { + // Should we return default exclusions in this case ? + return dataCollectorsNode; + } + + foreach (XmlNode node in dataCollectorsNode.ChildNodes) + { + foreach (XmlAttribute attribute in node.Attributes) + { + if (attribute.Name == "uri" && attribute.Value == "datacollector://microsoft/CodeCoverage/2.0") + { + return node; + } + } + } + + return null; + } + + private bool ShouldProcessCurrentExclusionClass(XmlNode node) + { + foreach (XmlAttribute attribute in node.Attributes) + { + if (attribute.Name == "mergeDefaults" && attribute.Value == "false") + { + return false; + } + } + + return true; + } - private void Merge() + private XmlNode ExtractNode(XmlNode doc, string path) { - foreach (var exclusionType in this.exclusions) + try { - exclusionType.AddExclusionNodes(this.defaultRunSettingsDocument); - exclusionType.AddExclusionNodes(this.runSettingsDocument); + return doc.SelectSingleNode(path); } + catch (XPathException ex) + { + EqtTrace.Error("CodeCoverageRunSettingsProcessor.ExtractNode: Cannot select single node \"{0}\".", ex.Message); + } + + return null; } - private void Replace() + private void AddNodes(XmlNode node, Tuple> exclusionClass) { - foreach (var exclusionType in this.exclusions) + foreach (XmlNode child in node.ChildNodes) { - exclusionType.ReplaceExclusionNodes(this.runSettingsDocument); + var key = child.OuterXml; + if (exclusionClass.Item2.ContainsKey(key)) + { + continue; + } + + exclusionClass.Item2.Add(key, child); } } + + private void ReplaceNodes(XmlNode node, Tuple> exclusionClass) + { + foreach (XmlNode child in node.ChildNodes) + { + if (!exclusionClass.Item2.ContainsKey(child.OuterXml)) + { + continue; + } + + exclusionClass.Item2.Remove(child.OuterXml); + } + + foreach (var child in exclusionClass.Item2.Values) + { + var imported = node.OwnerDocument.ImportNode(child, true); + node.AppendChild(imported); + } + + exclusionClass.Item2.Clear(); + } + #endregion } } From 3e1d20889f52ad8fdc59fb703de16860d195f139 Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Tue, 12 May 2020 18:31:46 +0200 Subject: [PATCH 03/15] Added partial path matching --- .../CodeCoverageRunSettingsProcessor.cs | 330 ++++++++++++++---- 1 file changed, 260 insertions(+), 70 deletions(-) diff --git a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs index a2a699204a..4ccae4e581 100644 --- a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs +++ b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs @@ -3,16 +3,93 @@ namespace Microsoft.VisualStudio.TestPlatform.Utilities { - using Microsoft.VisualStudio.TestPlatform.ObjectModel; using System; using System.Collections.Generic; + using System.Text; using System.Xml; using System.Xml.XPath; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + + /// + /// + /// public class CodeCoverageRunSettingsProcessor { + #region Type Members + /// + /// + /// + private class Exclusion + { + /// + /// + /// + private string path; + + /// + /// + /// + public IEnumerable PathComponents { get; } + + /// + /// + /// + public IDictionary ExclusionRules { get; } + + /// + /// + /// + public string Path + { + get + { + if (string.IsNullOrEmpty(this.path)) + { + this.path = Exclusion.BuildPath(this.PathComponents); + } + + return this.path; + } + } + + /// + /// + /// + /// + /// + public Exclusion(IEnumerable pathComponents) + { + this.path = string.Empty; + this.PathComponents = pathComponents; + this.ExclusionRules = new Dictionary(); + } + + /// + /// + /// + /// + /// + public static string BuildPath(IEnumerable pathComponents) + { + var path = "."; + + foreach (var component in pathComponents) + { + path += "/" + component; + } + + return path; + } + } + #endregion + #region Members - private static readonly string CodeCoverageCollectorDefaultSettings = + #region Default Settings String + /// + /// + /// + private static readonly string DefaultSettings = @"" + Environment.NewLine + @" " + Environment.NewLine + @" " + Environment.NewLine + @@ -89,105 +166,145 @@ public class CodeCoverageRunSettingsProcessor @" " + Environment.NewLine + @" " + Environment.NewLine + @""; + #endregion - private XmlDocument defaultRunSettingsDocument; + /// + /// + /// + private XmlDocument defaultSettingsDocument; - private XmlDocument runSettingsDocument; + /// + /// + /// + private XmlDocument currentSettingsDocument; - private IEnumerable>> exclusionClasses; + /// + /// + /// + private IEnumerable exclusions; #endregion #region Constructors & Helpers - public CodeCoverageRunSettingsProcessor(string runSettings) + /// + /// + /// + /// + /// + public CodeCoverageRunSettingsProcessor(string currentSettings) { - ValidateArg.NotNullOrEmpty(runSettings, nameof(runSettings)); + ValidateArg.NotNullOrEmpty(currentSettings, nameof(currentSettings)); - runSettingsDocument = new XmlDocument(); - runSettingsDocument.LoadXml(runSettings); + this.currentSettingsDocument = new XmlDocument(); + this.currentSettingsDocument.LoadXml(currentSettings); - this.Initialize(runSettingsDocument); + this.Initialize(currentSettingsDocument); } - public CodeCoverageRunSettingsProcessor(XmlDocument runSettingsDocument) + /// + /// + /// + /// + /// + public CodeCoverageRunSettingsProcessor(XmlDocument currentSettingsDocument) { - ValidateArg.NotNull(runSettingsDocument, nameof(runSettingsDocument)); + ValidateArg.NotNull(currentSettingsDocument, nameof(currentSettingsDocument)); - this.Initialize(runSettingsDocument); + this.Initialize(currentSettingsDocument); } - private void Initialize(XmlDocument runSettingsDocument) + /// + /// + /// + /// + /// + private void Initialize(XmlDocument currentSettingsDocument) { - this.runSettingsDocument = runSettingsDocument; + this.currentSettingsDocument = currentSettingsDocument; - defaultRunSettingsDocument = new XmlDocument(); - defaultRunSettingsDocument.LoadXml(CodeCoverageRunSettingsProcessor.CodeCoverageCollectorDefaultSettings); + this.defaultSettingsDocument = new XmlDocument(); + this.defaultSettingsDocument.LoadXml(CodeCoverageRunSettingsProcessor.DefaultSettings); - this.exclusionClasses = new List>>() + this.exclusions = new List { - new Tuple>( - @"./Configuration/CodeCoverage/ModulePaths/Exclude", - new Dictionary()), - - new Tuple>( - @"./Configuration/CodeCoverage/Attributes/Exclude", - new Dictionary()), - - new Tuple>( - @"./Configuration/CodeCoverage/Sources/Exclude", - new Dictionary()), - - new Tuple>( - @"./Configuration/CodeCoverage/Functions/Exclude", - new Dictionary()) + new Exclusion(new List { "ModulePaths", "Exclude" }), + new Exclusion(new List { "Attributes", "Exclude" }), + new Exclusion(new List { "Sources", "Exclude" }), + new Exclusion(new List { "Functions", "Exclude" }) }; } #endregion #region Public Interface + /// + /// + /// + /// + /// public string Process() { - var codeCoverageDataCollectorNode = GetCodeCoverageDataCollectorNode(); - if (codeCoverageDataCollectorNode == null) + var dataCollectorNode = this.GetDataCollectorNode(); + if (dataCollectorNode == null) { - // What do we do if we cannot extract code coverage data collectors node ? - return this.runSettingsDocument.OuterXml; + // No data collector settings for code coverage are found. + // TODO: Consider adding defaults nevertheless. + return this.currentSettingsDocument.OuterXml; } - foreach (var exclusionClass in this.exclusionClasses) + var codeCoveragePathComponents = new List() { "Configuration", "CodeCoverage" }; + var currentCodeCoverageNode = this.SelectNodeOrAddDefaults( + dataCollectorNode, + this.defaultSettingsDocument.FirstChild, + codeCoveragePathComponents); + + if (currentCodeCoverageNode == null) { - var node = this.ExtractNode(codeCoverageDataCollectorNode, exclusionClass.Item1); - if (node == null) - { - // What do we do if we cannot extract the specified class ? - continue; - } + return this.currentSettingsDocument.OuterXml; + } + + var defaultCodeCoverageNode = this.ExtractNode( + this.defaultSettingsDocument.FirstChild, + Exclusion.BuildPath(codeCoveragePathComponents)); - if (!this.ShouldProcessCurrentExclusionClass(node)) + foreach (var exclusion in this.exclusions) + { + var currentNode = this.SelectNodeOrAddDefaults( + currentCodeCoverageNode, + defaultCodeCoverageNode, + exclusion.PathComponents); + if (currentNode == null || !this.ShouldProcessCurrentExclusion(currentNode)) { continue; } - var defaultNode = this.ExtractNode(this.defaultRunSettingsDocument.FirstChild, exclusionClass.Item1); + var defaultNode = this.ExtractNode( + defaultCodeCoverageNode, + exclusion.Path); - AddNodes(defaultNode, exclusionClass); - AddNodes(node, exclusionClass); + this.AddNodes(defaultNode, exclusion); + this.AddNodes(currentNode, exclusion); - ReplaceNodes(node, exclusionClass); + this.ReplaceNodes(currentNode, exclusion); } - return this.runSettingsDocument.OuterXml; + return this.currentSettingsDocument.OuterXml; } #endregion #region Private Methods - private XmlNode GetCodeCoverageDataCollectorNode() + /// + /// + /// + /// + /// + private XmlNode GetDataCollectorNode() { - const string prefix = @"/RunSettings/DataCollectionRunSettings/DataCollectors"; + const string prefixPath = @"/RunSettings/DataCollectionRunSettings/DataCollectors"; + const string attributeName = "uri"; + const string attributeValue = "datacollector://microsoft/CodeCoverage/2.0"; - var dataCollectorsNode = this.ExtractNode(this.runSettingsDocument, prefix); + var dataCollectorsNode = this.ExtractNode(this.currentSettingsDocument, prefixPath); if (dataCollectorsNode == null) { - // Should we return default exclusions in this case ? return dataCollectorsNode; } @@ -195,7 +312,7 @@ private XmlNode GetCodeCoverageDataCollectorNode() { foreach (XmlAttribute attribute in node.Attributes) { - if (attribute.Name == "uri" && attribute.Value == "datacollector://microsoft/CodeCoverage/2.0") + if (attribute.Name == attributeName && attribute.Value == attributeValue) { return node; } @@ -205,11 +322,62 @@ private XmlNode GetCodeCoverageDataCollectorNode() return null; } - private bool ShouldProcessCurrentExclusionClass(XmlNode node) + /// + /// + /// + /// + /// + /// + /// + /// + private XmlNode SelectNodeOrAddDefaults( + XmlNode currentRootNode, + XmlNode defaultRootNode, + IEnumerable pathComponents) { + var currentNode = currentRootNode; + var partialPath = "."; + + foreach (var component in pathComponents) + { + var currentPathComponent = "/" + component; + + partialPath += currentPathComponent; + var tempNode = this.ExtractNode(currentNode, "." + currentPathComponent); + + if (tempNode == null) + { + var defaultNode = this.ExtractNode( + defaultRootNode, + partialPath); + + var importedChild = currentNode.OwnerDocument.ImportNode(defaultNode, true); + currentNode.AppendChild(importedChild); + + return null; + } + + currentNode = tempNode; + } + + return currentNode; + } + + /// + /// + /// + /// + /// + /// + /// + private bool ShouldProcessCurrentExclusion(XmlNode node) + { + const string attributeName = "mergeDefaults"; + const string attributeValue = "false"; + foreach (XmlAttribute attribute in node.Attributes) { - if (attribute.Name == "mergeDefaults" && attribute.Value == "false") + if (attribute.Name == attributeName && attribute.Value == attributeValue) { return false; } @@ -218,53 +386,75 @@ private bool ShouldProcessCurrentExclusionClass(XmlNode node) return true; } - private XmlNode ExtractNode(XmlNode doc, string path) + /// + /// + /// + /// + /// + /// + /// + /// + private XmlNode ExtractNode(XmlNode node, string path) { try { - return doc.SelectSingleNode(path); + return node.SelectSingleNode(path); } catch (XPathException ex) { - EqtTrace.Error("CodeCoverageRunSettingsProcessor.ExtractNode: Cannot select single node \"{0}\".", ex.Message); + EqtTrace.Error( + "CodeCoverageRunSettingsProcessor.ExtractNode: Cannot select single node \"{0}\".", + ex.Message); } return null; } - private void AddNodes(XmlNode node, Tuple> exclusionClass) + /// + /// + /// + /// + /// + /// + private void AddNodes(XmlNode node, Exclusion exclusion) { foreach (XmlNode child in node.ChildNodes) { var key = child.OuterXml; - if (exclusionClass.Item2.ContainsKey(key)) + if (exclusion.ExclusionRules.ContainsKey(key)) { continue; } - exclusionClass.Item2.Add(key, child); + exclusion.ExclusionRules.Add(key, child); } } - private void ReplaceNodes(XmlNode node, Tuple> exclusionClass) + /// + /// + /// + /// + /// + /// + private void ReplaceNodes(XmlNode node, Exclusion exclusion) { foreach (XmlNode child in node.ChildNodes) { - if (!exclusionClass.Item2.ContainsKey(child.OuterXml)) + if (!exclusion.ExclusionRules.ContainsKey(child.OuterXml)) { continue; } - exclusionClass.Item2.Remove(child.OuterXml); + exclusion.ExclusionRules.Remove(child.OuterXml); } - foreach (var child in exclusionClass.Item2.Values) + foreach (var child in exclusion.ExclusionRules.Values) { - var imported = node.OwnerDocument.ImportNode(child, true); - node.AppendChild(imported); + var importedChild = node.OwnerDocument.ImportNode(child, true); + node.AppendChild(importedChild); } - exclusionClass.Item2.Clear(); + exclusion.ExclusionRules.Clear(); } #endregion } From b4de43090a8e985054ea3e1117928f68d942f1cb Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Tue, 12 May 2020 18:40:18 +0200 Subject: [PATCH 04/15] Removed mergeDefaults attribute from template settings exclude tag --- .../CodeCoverageRunSettingsProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs index 4ccae4e581..a446279b76 100644 --- a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs +++ b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs @@ -94,7 +94,7 @@ public static string BuildPath(IEnumerable pathComponents) @" " + Environment.NewLine + @" " + Environment.NewLine + @" " + Environment.NewLine + - @" " + Environment.NewLine + + @" " + Environment.NewLine + @" .*CPPUnitTestFramework.*" + Environment.NewLine + @" .*vstest.console.*" + Environment.NewLine + @" .*microsoft.intellitrace.*" + Environment.NewLine + From 47171191c87b4530cf280d6a45bdfab9e8bce411 Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Wed, 13 May 2020 15:27:26 +0200 Subject: [PATCH 05/15] Added documentation --- .../CodeCoverageRunSettingsProcessor.cs | 297 +++++++++++------- 1 file changed, 177 insertions(+), 120 deletions(-) diff --git a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs index a446279b76..6396a22dd5 100644 --- a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs +++ b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs @@ -12,33 +12,33 @@ namespace Microsoft.VisualStudio.TestPlatform.Utilities using Microsoft.VisualStudio.TestPlatform.ObjectModel; /// - /// + /// Represents the run settings processor for code coverage data collectors. /// public class CodeCoverageRunSettingsProcessor { #region Type Members /// - /// + /// Represents the exclusion type for code coverage run settings. /// private class Exclusion { /// - /// + /// Represents the style path of the exclusion type. /// private string path; /// - /// + /// Gets the path components for the current exclusion type. /// public IEnumerable PathComponents { get; } /// - /// + /// Gets the exclusion rules for the current exclusion type. /// public IDictionary ExclusionRules { get; } /// - /// + /// Gets the actual exclusion type path generated from the individual path components. /// public string Path { @@ -54,10 +54,10 @@ public string Path } /// - /// + /// Constructs an object. /// /// - /// + /// The path split as components. public Exclusion(IEnumerable pathComponents) { this.path = string.Empty; @@ -66,10 +66,10 @@ public Exclusion(IEnumerable pathComponents) } /// - /// + /// Assembles a relative path from the path given components. /// /// - /// + /// A relative path built from path components. public static string BuildPath(IEnumerable pathComponents) { var path = "."; @@ -87,113 +87,117 @@ public static string BuildPath(IEnumerable pathComponents) #region Members #region Default Settings String /// - /// + /// Represents the default settings for the code coverage data collector. /// - private static readonly string DefaultSettings = - @"" + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" .*CPPUnitTestFramework.*" + Environment.NewLine + - @" .*vstest.console.*" + Environment.NewLine + - @" .*microsoft.intellitrace.*" + Environment.NewLine + - @" .*testhost.*" + Environment.NewLine + - @" .*datacollector.*" + Environment.NewLine + - @" .*microsoft.teamfoundation.testplatform.*" + Environment.NewLine + - @" .*microsoft.visualstudio.testplatform.*" + Environment.NewLine + - @" .*microsoft.visualstudio.testwindow.*" + Environment.NewLine + - @" .*microsoft.visualstudio.mstest.*" + Environment.NewLine + - @" .*microsoft.visualstudio.qualitytools.*" + Environment.NewLine + - @" .*microsoft.vssdk.testhostadapter.*" + Environment.NewLine + - @" .*microsoft.vssdk.testhostframework.*" + Environment.NewLine + - @" .*qtagent32.*" + Environment.NewLine + - @" .*msvcr.*dll$" + Environment.NewLine + - @" .*msvcp.*dll$" + Environment.NewLine + - @" .*clr.dll$" + Environment.NewLine + - @" .*clr.ni.dll$" + Environment.NewLine + - @" .*clrjit.dll$" + Environment.NewLine + - @" .*clrjit.ni.dll$" + Environment.NewLine + - @" .*mscoree.dll$" + Environment.NewLine + - @" .*mscoreei.dll$" + Environment.NewLine + - @" .*mscoreei.ni.dll$" + Environment.NewLine + - @" .*mscorlib.dll$" + Environment.NewLine + - @" .*mscorlib.ni.dll$" + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" True" + Environment.NewLine + - @" True" + Environment.NewLine + - @" True" + Environment.NewLine + - @" false" + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" ^std::.*" + Environment.NewLine + - @" ^ATL::.*" + Environment.NewLine + - @" .*::__GetTestMethodInfo.*" + Environment.NewLine + - @" .*__CxxPureMSILEntry.*" + Environment.NewLine + - @" ^Microsoft::VisualStudio::CppCodeCoverageFramework::.*" + Environment.NewLine + - @" ^Microsoft::VisualStudio::CppUnitTestFramework::.*" + Environment.NewLine + - @" .*::YOU_CAN_ONLY_DESIGNATE_ONE_.*" + Environment.NewLine + - @" ^__.*" + Environment.NewLine + - @" .*::__.*" + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" ^System.Diagnostics.DebuggerHiddenAttribute$" + Environment.NewLine + - @" ^System.Diagnostics.DebuggerNonUserCodeAttribute$" + Environment.NewLine + - @" ^System.Runtime.CompilerServices.CompilerGeneratedAttribute$" + Environment.NewLine + - @" ^System.CodeDom.Compiler.GeneratedCodeAttribute$" + Environment.NewLine + - @" ^System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute$" + Environment.NewLine + - @" ^Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode.*" + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" .*\\atlmfc\\.*" + Environment.NewLine + - @" .*\\vctools\\.*" + Environment.NewLine + - @" .*\\public\\sdk\\.*" + Environment.NewLine + - @" .*\\externalapis\\.*" + Environment.NewLine + - @" .*\\microsoft sdks\\.*" + Environment.NewLine + - @" .*\\vc\\include\\.*" + Environment.NewLine + - @" .*\\msclr\\.*" + Environment.NewLine + - @" .*\\ucrt\\.*" + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @" " + Environment.NewLine + - @""; + private static readonly string DefaultSettings = 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$", + @" ", + @" ", + @" 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\\.*", + @" ", + @" ", + @" ", + @" ", + @" ", + @" ", + @""); #endregion /// - /// + /// Represents the default settings loaded as an . /// private XmlDocument defaultSettingsDocument; /// - /// + /// Represents the current settings loaded as an . /// private XmlDocument currentSettingsDocument; /// - /// + /// Represents a list of exclusion types tracked by this processor. /// private IEnumerable exclusions; #endregion #region Constructors & Helpers /// - /// + /// Constructs an object. /// /// - /// + /// + /// The current settings for the code coverage data collector. + /// public CodeCoverageRunSettingsProcessor(string currentSettings) { ValidateArg.NotNullOrEmpty(currentSettings, nameof(currentSettings)); + // Load current settings from string. this.currentSettingsDocument = new XmlDocument(); this.currentSettingsDocument.LoadXml(currentSettings); @@ -201,10 +205,13 @@ public CodeCoverageRunSettingsProcessor(string currentSettings) } /// - /// + /// Constructs an object. /// /// - /// + /// + /// The current settings for the code coverage data collector loaded as an + /// . + /// public CodeCoverageRunSettingsProcessor(XmlDocument currentSettingsDocument) { ValidateArg.NotNull(currentSettingsDocument, nameof(currentSettingsDocument)); @@ -213,17 +220,22 @@ public CodeCoverageRunSettingsProcessor(XmlDocument currentSettingsDocument) } /// - /// + /// Finishes initialization of the . /// /// - /// + /// + /// The current settings for the code coverage data collector loaded as an + /// . + /// private void Initialize(XmlDocument currentSettingsDocument) { this.currentSettingsDocument = currentSettingsDocument; + // Load default settings from string. this.defaultSettingsDocument = new XmlDocument(); this.defaultSettingsDocument.LoadXml(CodeCoverageRunSettingsProcessor.DefaultSettings); + // Create the exclusion type list. this.exclusions = new List { new Exclusion(new List { "ModulePaths", "Exclude" }), @@ -236,12 +248,13 @@ private void Initialize(XmlDocument currentSettingsDocument) #region Public Interface /// - /// + /// Processes the current settings for the code coverage data collector. /// /// - /// + /// An updated version of the current run settings. public string Process() { + // Get the code coverage data collector node. var dataCollectorNode = this.GetDataCollectorNode(); if (dataCollectorNode == null) { @@ -250,6 +263,9 @@ public string Process() return this.currentSettingsDocument.OuterXml; } + // 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() { "Configuration", "CodeCoverage" }; var currentCodeCoverageNode = this.SelectNodeOrAddDefaults( dataCollectorNode, @@ -261,29 +277,41 @@ public string Process() return this.currentSettingsDocument.OuterXml; } + // Get the code coverage node from the default settings. var defaultCodeCoverageNode = this.ExtractNode( this.defaultSettingsDocument.FirstChild, Exclusion.BuildPath(codeCoveragePathComponents)); foreach (var exclusion in this.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.PathComponents); + + // 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 || !this.ShouldProcessCurrentExclusion(currentNode)) { continue; } + // Extract the node from the default settings. var defaultNode = this.ExtractNode( defaultCodeCoverageNode, exclusion.Path); + // Add nodes from both the current and the default settings to current exclusion + // type's exclusion rules. this.AddNodes(defaultNode, exclusion); this.AddNodes(currentNode, exclusion); - this.ReplaceNodes(currentNode, exclusion); + // Merge both the current and the default settings for the current exclusion rule. + this.MergeNodes(currentNode, exclusion); } return this.currentSettingsDocument.OuterXml; @@ -292,22 +320,26 @@ public string Process() #region Private Methods /// - /// + /// Gets the code coverage data collector node from the current settings document. /// /// - /// + /// + /// The code coverage data collector node from the current settings document. + /// private XmlNode GetDataCollectorNode() { const string prefixPath = @"/RunSettings/DataCollectionRunSettings/DataCollectors"; const string attributeName = "uri"; const string attributeValue = "datacollector://microsoft/CodeCoverage/2.0"; + // Extract all data collectors. var dataCollectorsNode = this.ExtractNode(this.currentSettingsDocument, prefixPath); if (dataCollectorsNode == null) { return dataCollectorsNode; } + // Identify the correct data collector node for code coverage. foreach (XmlNode node in dataCollectorsNode.ChildNodes) { foreach (XmlAttribute attribute in node.Attributes) @@ -323,13 +355,20 @@ private XmlNode GetDataCollectorNode() } /// - /// + /// 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, @@ -342,9 +381,14 @@ private XmlNode SelectNodeOrAddDefaults( { var currentPathComponent = "/" + component; + // Append the current path component to the partial path. partialPath += currentPathComponent; + + // Extract the node corresponding to the latest path component. var tempNode = this.ExtractNode(currentNode, "." + currentPathComponent); + // If the current node extraction is unsuccessful then add the corresponding + // default settings node and bail out. if (tempNode == null) { var defaultNode = this.ExtractNode( @@ -357,6 +401,8 @@ private XmlNode SelectNodeOrAddDefaults( return null; } + // Node corresponding to the latest path component is the new root node for the + // next extraction. currentNode = tempNode; } @@ -364,12 +410,14 @@ private XmlNode SelectNodeOrAddDefaults( } /// - /// + /// 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"; @@ -387,13 +435,13 @@ private bool ShouldProcessCurrentExclusion(XmlNode node) } /// - /// + /// 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 @@ -411,33 +459,39 @@ private XmlNode ExtractNode(XmlNode node, string path) } /// - /// + /// Adds all children nodes of the current root to the current exclusion type's exclusion + /// rules cache. /// /// - /// - /// + /// The root node. + /// The exclusion rule. private void AddNodes(XmlNode node, Exclusion exclusion) { foreach (XmlNode child in node.ChildNodes) { var key = child.OuterXml; + + // Ignore keys that are already present in the current exclusion type's exclusion + // rules cache. if (exclusion.ExclusionRules.ContainsKey(key)) { continue; } + // Add the current exclusion rule to the exclusion type's cache. exclusion.ExclusionRules.Add(key, child); } } /// - /// + /// Merges the current settings rules with the default settings rules. /// /// - /// - /// - private void ReplaceNodes(XmlNode node, Exclusion exclusion) + /// The root node. + /// The exclusion rule. + private void MergeNodes(XmlNode node, Exclusion exclusion) { + // Iterate through all the children nodes of the given root. foreach (XmlNode child in node.ChildNodes) { if (!exclusion.ExclusionRules.ContainsKey(child.OuterXml)) @@ -445,11 +499,14 @@ private void ReplaceNodes(XmlNode node, Exclusion exclusion) continue; } + // Remove exclusion rule from the current exclusion type's cache. exclusion.ExclusionRules.Remove(child.OuterXml); } + // Iterate through remaining items in the current exclusion type cache. foreach (var child in exclusion.ExclusionRules.Values) { + // Import any remaining items in the current settings document. var importedChild = node.OwnerDocument.ImportNode(child, true); node.AppendChild(importedChild); } From a82fb7b5cf5d2e0eff5c27b842ef7629577b513e Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Wed, 20 May 2020 14:40:49 +0200 Subject: [PATCH 06/15] Inserted the settings-processing logic on the data collector level --- ...oft.VisualStudio.TraceDataCollector.csproj | 1 + .../DynamicCoverageDataCollectorImpl.cs | 4 + .../DataCollection/DataCollectionManager.cs | 2 - .../CodeCoverageRunSettingsProcessor.cs | 139 +++++++++--------- 4 files changed, 73 insertions(+), 73 deletions(-) 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..a0dd4c0422 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,6 +108,9 @@ public virtual void Initialize( IDataCollectionSink dataSink, IDataCollectionLogger logger) { + var processor = new CodeCoverageRunSettingsProcessor(configurationElement); + configurationElement = (XmlElement)processor.ProcessToNode(); + EqtTrace.Info("DynamicCoverageDataCollectorImpl.Initialize: Initialize configuration. "); if (string.IsNullOrEmpty(configurationElement?.InnerXml)) { diff --git a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs index 9fe6e31abd..d053b643fa 100644 --- a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs +++ b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs @@ -154,8 +154,6 @@ public IDictionary InitializeDataCollectors(string settingsXml) ValidateArg.NotNull(settingsXml, "settingsXml"); - settingsXml = (new CodeCoverageRunSettingsProcessor(settingsXml)).Process(); - var sessionId = new SessionId(Guid.NewGuid()); var dataCollectionContext = new DataCollectionContext(sessionId); this.dataCollectionEnvironmentContext = DataCollectionEnvironmentContext.CreateForLocalEnvironment(dataCollectionContext); diff --git a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs index 6396a22dd5..c0def9b41e 100644 --- a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs +++ b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs @@ -57,7 +57,7 @@ public string Path /// Constructs an object. /// /// - /// The path split as components. + /// The path split in components. public Exclusion(IEnumerable pathComponents) { this.path = string.Empty; @@ -66,7 +66,7 @@ public Exclusion(IEnumerable pathComponents) } /// - /// Assembles a relative path from the path given components. + /// Assembles a relative path from the path given as components. /// /// /// A relative path built from path components. @@ -170,14 +170,14 @@ public static string BuildPath(IEnumerable pathComponents) #endregion /// - /// Represents the default settings loaded as an . + /// Represents the default settings loaded as an . /// - private XmlDocument defaultSettingsDocument; + private XmlNode defaultSettingsRootNode; /// - /// Represents the current settings loaded as an . + /// Represents the current settings loaded as an . /// - private XmlDocument currentSettingsDocument; + private XmlNode currentSettingsRootNode; /// /// Represents a list of exclusion types tracked by this processor. @@ -198,10 +198,25 @@ public CodeCoverageRunSettingsProcessor(string currentSettings) ValidateArg.NotNullOrEmpty(currentSettings, nameof(currentSettings)); // Load current settings from string. - this.currentSettingsDocument = new XmlDocument(); - this.currentSettingsDocument.LoadXml(currentSettings); + var document = new XmlDocument(); + document.LoadXml(currentSettings); - this.Initialize(currentSettingsDocument); + this.Initialize(document.DocumentElement); + } + + /// + /// Constructs an object. + /// + /// + /// + /// The current settings for the code coverage data collector loaded as an + /// . + /// + public CodeCoverageRunSettingsProcessor(XmlNode currentSettingsRootNode) + { + ValidateArg.NotNull(currentSettingsRootNode, nameof(currentSettingsRootNode)); + + this.Initialize(currentSettingsRootNode); } /// @@ -210,30 +225,32 @@ public CodeCoverageRunSettingsProcessor(string currentSettings) /// /// /// The current settings for the code coverage data collector loaded as an - /// . + /// /// public CodeCoverageRunSettingsProcessor(XmlDocument currentSettingsDocument) { ValidateArg.NotNull(currentSettingsDocument, nameof(currentSettingsDocument)); - this.Initialize(currentSettingsDocument); + this.Initialize(currentSettingsDocument.DocumentElement); } /// /// Finishes initialization of the . /// /// - /// + /// /// The current settings for the code coverage data collector loaded as an - /// . + /// . /// - private void Initialize(XmlDocument currentSettingsDocument) + private void Initialize(XmlNode currentSettingsRootNode) { - this.currentSettingsDocument = currentSettingsDocument; + this.currentSettingsRootNode = currentSettingsRootNode; // Load default settings from string. - this.defaultSettingsDocument = new XmlDocument(); - this.defaultSettingsDocument.LoadXml(CodeCoverageRunSettingsProcessor.DefaultSettings); + var document = new XmlDocument(); + document.LoadXml(CodeCoverageRunSettingsProcessor.DefaultSettings); + + this.defaultSettingsRootNode = document.DocumentElement.FirstChild; // Create the exclusion type list. this.exclusions = new List @@ -251,35 +268,54 @@ private void Initialize(XmlDocument currentSettingsDocument) /// Processes the current settings for the code coverage data collector. /// /// - /// An updated version of the current run settings. - public string Process() + /// + /// An updated version of the current run settings as a . + /// + public string ProcessToString() { - // Get the code coverage data collector node. - var dataCollectorNode = this.GetDataCollectorNode(); - if (dataCollectorNode == null) - { - // No data collector settings for code coverage are found. - // TODO: Consider adding defaults nevertheless. - return this.currentSettingsDocument.OuterXml; - } + this.Process(); + return this.currentSettingsRootNode.OuterXml; + } + + /// + /// Processes the current settings for the code coverage data collector. + /// + /// + /// + /// An updated version of the current run settings as a . + /// + public XmlNode ProcessToNode() + { + this.Process(); + return this.currentSettingsRootNode; + } + #endregion + #region Private Methods + /// + /// Processes the current settings for the code coverage data collector. + /// + /// + /// An updated version of the current run settings. + private void Process() + { // 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() { "Configuration", "CodeCoverage" }; + var codeCoveragePathComponents = new List() { "CodeCoverage" }; var currentCodeCoverageNode = this.SelectNodeOrAddDefaults( - dataCollectorNode, - this.defaultSettingsDocument.FirstChild, + this.currentSettingsRootNode, + this.defaultSettingsRootNode, codeCoveragePathComponents); if (currentCodeCoverageNode == null) { - return this.currentSettingsDocument.OuterXml; + return; } // Get the code coverage node from the default settings. var defaultCodeCoverageNode = this.ExtractNode( - this.defaultSettingsDocument.FirstChild, + this.defaultSettingsRootNode, Exclusion.BuildPath(codeCoveragePathComponents)); foreach (var exclusion in this.exclusions) @@ -313,45 +349,6 @@ public string Process() // Merge both the current and the default settings for the current exclusion rule. this.MergeNodes(currentNode, exclusion); } - - return this.currentSettingsDocument.OuterXml; - } - #endregion - - #region Private Methods - /// - /// Gets the code coverage data collector node from the current settings document. - /// - /// - /// - /// The code coverage data collector node from the current settings document. - /// - private XmlNode GetDataCollectorNode() - { - const string prefixPath = @"/RunSettings/DataCollectionRunSettings/DataCollectors"; - const string attributeName = "uri"; - const string attributeValue = "datacollector://microsoft/CodeCoverage/2.0"; - - // Extract all data collectors. - var dataCollectorsNode = this.ExtractNode(this.currentSettingsDocument, prefixPath); - if (dataCollectorsNode == null) - { - return dataCollectorsNode; - } - - // Identify the correct data collector node for code coverage. - foreach (XmlNode node in dataCollectorsNode.ChildNodes) - { - foreach (XmlAttribute attribute in node.Attributes) - { - if (attribute.Name == attributeName && attribute.Value == attributeValue) - { - return node; - } - } - } - - return null; } /// From 0b98be943b071bf2a740c43f87d87f9c084502c1 Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Tue, 26 May 2020 18:23:34 +0200 Subject: [PATCH 07/15] Reworked settings processor interface --- .../CodeCoverageRunSettingsProcessor.cs | 119 ++++++------------ 1 file changed, 41 insertions(+), 78 deletions(-) diff --git a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs index c0def9b41e..7b041dd486 100644 --- a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs +++ b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs @@ -174,11 +174,6 @@ public static string BuildPath(IEnumerable pathComponents) /// private XmlNode defaultSettingsRootNode; - /// - /// Represents the current settings loaded as an . - /// - private XmlNode currentSettingsRootNode; - /// /// Represents a list of exclusion types tracked by this processor. /// @@ -189,63 +184,8 @@ public static string BuildPath(IEnumerable pathComponents) /// /// Constructs an object. /// - /// - /// - /// The current settings for the code coverage data collector. - /// - public CodeCoverageRunSettingsProcessor(string currentSettings) - { - ValidateArg.NotNullOrEmpty(currentSettings, nameof(currentSettings)); - - // Load current settings from string. - var document = new XmlDocument(); - document.LoadXml(currentSettings); - - this.Initialize(document.DocumentElement); - } - - /// - /// Constructs an object. - /// - /// - /// - /// The current settings for the code coverage data collector loaded as an - /// . - /// - public CodeCoverageRunSettingsProcessor(XmlNode currentSettingsRootNode) - { - ValidateArg.NotNull(currentSettingsRootNode, nameof(currentSettingsRootNode)); - - this.Initialize(currentSettingsRootNode); - } - - /// - /// Constructs an object. - /// - /// - /// - /// The current settings for the code coverage data collector loaded as an - /// - /// - public CodeCoverageRunSettingsProcessor(XmlDocument currentSettingsDocument) - { - ValidateArg.NotNull(currentSettingsDocument, nameof(currentSettingsDocument)); - - this.Initialize(currentSettingsDocument.DocumentElement); - } - - /// - /// Finishes initialization of the . - /// - /// - /// - /// The current settings for the code coverage data collector loaded as an - /// . - /// - private void Initialize(XmlNode currentSettingsRootNode) + public CodeCoverageRunSettingsProcessor() { - this.currentSettingsRootNode = currentSettingsRootNode; - // Load default settings from string. var document = new XmlDocument(); document.LoadXml(CodeCoverageRunSettingsProcessor.DefaultSettings); @@ -268,49 +208,68 @@ private void Initialize(XmlNode currentSettingsRootNode) /// Processes the current settings for the code coverage data collector. /// /// - /// - /// An updated version of the current run settings as a . - /// - public string ProcessToString() + /// The code coverage settings. + /// + /// An updated version of the current run settings. + public XmlNode Process(string currentSettings) { - this.Process(); - return this.currentSettingsRootNode.OuterXml; + 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. /// /// - /// - /// An updated version of the current run settings as a . - /// - public XmlNode ProcessToNode() + /// + /// The code coverage settings document. + /// + /// + /// An updated version of the current run settings. + public XmlNode Process(XmlDocument currentSettingsDocument) { - this.Process(); - return this.currentSettingsRootNode; + if (currentSettingsDocument == null) + { + return null; + } + + return this.Process(currentSettingsDocument.DocumentElement); } - #endregion - #region Private Methods /// /// Processes the current settings for the code coverage data collector. /// /// + /// The code coverage root element. + /// /// An updated version of the current run settings. - private void Process() + 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( - this.currentSettingsRootNode, + currentSettingsRootNode, this.defaultSettingsRootNode, codeCoveragePathComponents); if (currentCodeCoverageNode == null) { - return; + return currentSettingsRootNode; } // Get the code coverage node from the default settings. @@ -349,8 +308,12 @@ private void Process() // Merge both the current and the default settings for the current exclusion rule. this.MergeNodes(currentNode, exclusion); } + + 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 From f6c566fd572a04c8ffe29690bcf0cdec8fc7e777 Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Tue, 26 May 2020 19:29:33 +0200 Subject: [PATCH 08/15] Fixed compilation error --- .../VanguardCollector/DynamicCoverageDataCollectorImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs b/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs index a0dd4c0422..846bfa3ffd 100644 --- a/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs +++ b/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs @@ -108,8 +108,8 @@ public virtual void Initialize( IDataCollectionSink dataSink, IDataCollectionLogger logger) { - var processor = new CodeCoverageRunSettingsProcessor(configurationElement); - configurationElement = (XmlElement)processor.ProcessToNode(); + var processor = new CodeCoverageRunSettingsProcessor(); + configurationElement = (XmlElement)processor.Process(configurationElement); EqtTrace.Info("DynamicCoverageDataCollectorImpl.Initialize: Initialize configuration. "); if (string.IsNullOrEmpty(configurationElement?.InnerXml)) From bd6369328eb71d1d7986fdfc8b629720ceef1a31 Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Wed, 27 May 2020 10:28:02 +0200 Subject: [PATCH 09/15] Enhanced default code coverage config string --- .../CodeCoverageRunSettingsProcessor.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs index 7b041dd486..2128f85b50 100644 --- a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs +++ b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs @@ -120,6 +120,8 @@ public static string BuildPath(IEnumerable pathComponents) @" .*mscoreei.ni.dll$", @" .*mscorlib.dll$", @" .*mscorlib.ni.dll$", + @" .*cryptbase.dll$", + @" .*bcryptPrimitives.dll$", @" ", @" ", @" True", @@ -144,7 +146,7 @@ public static string BuildPath(IEnumerable pathComponents) @" ", @" ^System.Diagnostics.DebuggerHiddenAttribute$", @" ^System.Diagnostics.DebuggerNonUserCodeAttribute$", - @" ^System.Runtime.CompilerServices.CompilerGeneratedAttribute$", + @" System.Runtime.CompilerServices.CompilerGeneratedAttribute$", @" ^System.CodeDom.Compiler.GeneratedCodeAttribute$", @" ^System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute$", @" ^Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode.*", From 771c5d868e4860004fca53630dd15f16aaa30c13 Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Wed, 27 May 2020 14:22:21 +0200 Subject: [PATCH 10/15] Fixed unit tests --- .../DynamicCoverageDataCollectorImplTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/DataCollectors/TraceDataCollector.UnitTests/DynamicCoverageDataCollectorImplTests.cs b/test/DataCollectors/TraceDataCollector.UnitTests/DynamicCoverageDataCollectorImplTests.cs index 82d2ee58aa..cd0ff00f29 100644 --- a/test/DataCollectors/TraceDataCollector.UnitTests/DynamicCoverageDataCollectorImplTests.cs +++ b/test/DataCollectors/TraceDataCollector.UnitTests/DynamicCoverageDataCollectorImplTests.cs @@ -83,6 +83,7 @@ public void InitializeShouldCreateDefaultCodeCoverageSettingsIfConfigElementIsNu } [TestMethod] + [Ignore] public void InitializeShouldInitializeVanguardWithRightCoverageSettings() { var expectedContent = "CoverageSettingsContent"; From b72b9b853fa325a59b686dd0563c99777d9243e6 Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Wed, 27 May 2020 15:30:44 +0200 Subject: [PATCH 11/15] Fixed code review comments --- .../CodeCoverageRunSettingsProcessor.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs index 2128f85b50..6d64217baa 100644 --- a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs +++ b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs @@ -72,14 +72,16 @@ public Exclusion(IEnumerable pathComponents) /// A relative path built from path components. public static string BuildPath(IEnumerable pathComponents) { - var path = "."; + var sb = new StringBuilder(); + sb.Append("."); foreach (var component in pathComponents) { - path += "/" + component; + sb.Append("/"); + sb.Append(component); } - return path; + return sb.ToString(); } } #endregion @@ -337,14 +339,16 @@ private XmlNode SelectNodeOrAddDefaults( IEnumerable pathComponents) { var currentNode = currentRootNode; - var partialPath = "."; + var partialPath = new StringBuilder(); + + partialPath.Append("."); foreach (var component in pathComponents) { var currentPathComponent = "/" + component; // Append the current path component to the partial path. - partialPath += currentPathComponent; + partialPath.Append(currentPathComponent); // Extract the node corresponding to the latest path component. var tempNode = this.ExtractNode(currentNode, "." + currentPathComponent); @@ -355,7 +359,7 @@ private XmlNode SelectNodeOrAddDefaults( { var defaultNode = this.ExtractNode( defaultRootNode, - partialPath); + partialPath.ToString()); var importedChild = currentNode.OwnerDocument.ImportNode(defaultNode, true); currentNode.AppendChild(importedChild); From 0ca7f81c9451d84f2f967e63b323fbe485e5e322 Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Wed, 27 May 2020 15:46:54 +0200 Subject: [PATCH 12/15] Removed unnecessary using directive --- .../DataCollection/DataCollectionManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs index d053b643fa..dbebbe63f9 100644 --- a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs +++ b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionManager.cs @@ -13,7 +13,6 @@ namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollector using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework; using Microsoft.VisualStudio.TestPlatform.Common.Logging; using Microsoft.VisualStudio.TestPlatform.Common.Utilities; - using Microsoft.VisualStudio.TestPlatform.Utilities; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; From 2e2b9f61cf677ee1a2122c4ce1cb8683850aabac Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Thu, 28 May 2020 20:38:26 +0200 Subject: [PATCH 13/15] Added unit tests --- .../CodeCoverageRunSettingsProcessorTests.cs | 308 ++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageRunSettingsProcessorTests.cs diff --git a/test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageRunSettingsProcessorTests.cs b/test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageRunSettingsProcessorTests.cs new file mode 100644 index 0000000000..3a2c254828 --- /dev/null +++ b/test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageRunSettingsProcessorTests.cs @@ -0,0 +1,308 @@ +// 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.Xml; + using Microsoft.VisualStudio.TestPlatform.Utilities; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class CodeCoverageRunSettingsProcessorTests + { + private static readonly string DefaultSettings = 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$", + @" ", + @" ", + @" 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\\.*", + @" ", + @" ", + @" ", + @" ", + @" ", + @""); + + private CodeCoverageRunSettingsProcessor processor; + + private XmlDocument document; + + 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); + } + + public CodeCoverageRunSettingsProcessorTests() + { + this.processor = new CodeCoverageRunSettingsProcessor(); + this.document = new XmlDocument(); + + this.document.LoadXml(CodeCoverageRunSettingsProcessorTests.DefaultSettings); + } + + [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.document.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.document.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"); + } + + [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"); + } + } +} From 6ead129abe228ee551ac4e77d81c5c4d1f6b3e68 Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Mon, 1 Jun 2020 09:21:06 +0200 Subject: [PATCH 14/15] Fixed code review comments --- .../DynamicCoverageDataCollectorImpl.cs | 6 +- .../CodeCoverageRunSettingsProcessor.cs | 296 ++++-------------- .../DynamicCoverageDataCollectorImplTests.cs | 71 ++++- .../CodeCoverageRunSettingsProcessorTests.cs | 252 +++++++-------- .../DefaultCodeCoverageConfig.xml | 77 +++++ ...ft.TestPlatform.Utilities.UnitTests.csproj | 3 + 6 files changed, 330 insertions(+), 375 deletions(-) create mode 100644 test/Microsoft.TestPlatform.Utilities.UnitTests/DefaultCodeCoverageConfig.xml diff --git a/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs b/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs index 846bfa3ffd..78bd38dd42 100644 --- a/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs +++ b/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs @@ -108,13 +108,15 @@ public virtual void Initialize( IDataCollectionSink dataSink, IDataCollectionLogger logger) { - var processor = new CodeCoverageRunSettingsProcessor(); + var defaultConfigurationElement = DynamicCoverageDataCollectorImpl.GetDefaultConfiguration(); + + var processor = new CodeCoverageRunSettingsProcessor(defaultConfigurationElement); configurationElement = (XmlElement)processor.Process(configurationElement); 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 index 6d64217baa..6e63832c43 100644 --- a/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs +++ b/src/Microsoft.TestPlatform.Utilities/CodeCoverageRunSettingsProcessor.cs @@ -5,6 +5,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Utilities { using System; using System.Collections.Generic; + using System.Linq; using System.Text; using System.Xml; using System.Xml.XPath; @@ -16,194 +17,27 @@ namespace Microsoft.VisualStudio.TestPlatform.Utilities /// public class CodeCoverageRunSettingsProcessor { - #region Type Members - /// - /// Represents the exclusion type for code coverage run settings. - /// - private class Exclusion - { - /// - /// Represents the style path of the exclusion type. - /// - private string path; - - /// - /// Gets the path components for the current exclusion type. - /// - public IEnumerable PathComponents { get; } - - /// - /// Gets the exclusion rules for the current exclusion type. - /// - public IDictionary ExclusionRules { get; } - - /// - /// Gets the actual exclusion type path generated from the individual path components. - /// - public string Path - { - get - { - if (string.IsNullOrEmpty(this.path)) - { - this.path = Exclusion.BuildPath(this.PathComponents); - } - - return this.path; - } - } - - /// - /// Constructs an object. - /// - /// - /// The path split in components. - public Exclusion(IEnumerable pathComponents) - { - this.path = string.Empty; - this.PathComponents = pathComponents; - this.ExclusionRules = new Dictionary(); - } - - /// - /// Assembles a relative path from the path given as components. - /// - /// - /// A relative path built from path components. - public static string BuildPath(IEnumerable pathComponents) - { - var sb = new StringBuilder(); - sb.Append("."); - - foreach (var component in pathComponents) - { - sb.Append("/"); - sb.Append(component); - } - - return sb.ToString(); - } - } - #endregion - #region Members - #region Default Settings String - /// - /// Represents the default settings for the code coverage data collector. - /// - private static readonly string DefaultSettings = 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$", - @" ", - @" ", - @" 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\\.*", - @" ", - @" ", - @" ", - @" ", - @" ", - @" ", - @""); - #endregion - /// /// Represents the default settings loaded as an . /// private XmlNode defaultSettingsRootNode; - - /// - /// Represents a list of exclusion types tracked by this processor. - /// - private IEnumerable exclusions; #endregion #region Constructors & Helpers /// /// Constructs an object. /// - public CodeCoverageRunSettingsProcessor() + /// + /// The default settings root node. + public CodeCoverageRunSettingsProcessor(XmlNode defaultSettingsRootNode) { - // Load default settings from string. - var document = new XmlDocument(); - document.LoadXml(CodeCoverageRunSettingsProcessor.DefaultSettings); - - this.defaultSettingsRootNode = document.DocumentElement.FirstChild; - - // Create the exclusion type list. - this.exclusions = new List + if (defaultSettingsRootNode == null) { - new Exclusion(new List { "ModulePaths", "Exclude" }), - new Exclusion(new List { "Attributes", "Exclude" }), - new Exclusion(new List { "Sources", "Exclude" }), - new Exclusion(new List { "Functions", "Exclude" }) - }; + throw new ArgumentNullException("Default settings root node is null."); + } + + this.defaultSettingsRootNode = defaultSettingsRootNode; } #endregion @@ -271,6 +105,9 @@ public XmlNode Process(XmlNode 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; @@ -279,9 +116,18 @@ public XmlNode Process(XmlNode currentSettingsRootNode) // Get the code coverage node from the default settings. var defaultCodeCoverageNode = this.ExtractNode( this.defaultSettingsRootNode, - Exclusion.BuildPath(codeCoveragePathComponents)); + this.BuildPath(codeCoveragePathComponents)); - foreach (var exclusion in this.exclusions) + // 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 @@ -290,11 +136,11 @@ public XmlNode Process(XmlNode currentSettingsRootNode) var currentNode = this.SelectNodeOrAddDefaults( currentCodeCoverageNode, defaultCodeCoverageNode, - exclusion.PathComponents); + 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 || !this.ShouldProcessCurrentExclusion(currentNode)) + if (currentNode == null) { continue; } @@ -302,15 +148,10 @@ public XmlNode Process(XmlNode currentSettingsRootNode) // Extract the node from the default settings. var defaultNode = this.ExtractNode( defaultCodeCoverageNode, - exclusion.Path); - - // Add nodes from both the current and the default settings to current exclusion - // type's exclusion rules. - this.AddNodes(defaultNode, exclusion); - this.AddNodes(currentNode, exclusion); + this.BuildPath(exclusion)); - // Merge both the current and the default settings for the current exclusion rule. - this.MergeNodes(currentNode, exclusion); + // Merge the current and default settings for the current exclusion rule. + this.MergeNodes(currentNode, defaultNode); } return currentSettingsRootNode; @@ -336,7 +177,7 @@ public XmlNode Process(XmlNode currentSettingsRootNode) private XmlNode SelectNodeOrAddDefaults( XmlNode currentRootNode, XmlNode defaultRootNode, - IEnumerable pathComponents) + IList pathComponents) { var currentNode = currentRootNode; var partialPath = new StringBuilder(); @@ -353,6 +194,12 @@ private XmlNode SelectNodeOrAddDefaults( // 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) @@ -387,11 +234,14 @@ private XmlNode SelectNodeOrAddDefaults( private bool ShouldProcessCurrentExclusion(XmlNode node) { const string attributeName = "mergeDefaults"; - const string attributeValue = "false"; foreach (XmlAttribute attribute in node.Attributes) { - if (attribute.Name == attributeName && attribute.Value == attributeValue) + // 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; } @@ -400,6 +250,16 @@ private bool ShouldProcessCurrentExclusion(XmlNode node) 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. /// @@ -425,59 +285,33 @@ private XmlNode ExtractNode(XmlNode node, string path) } /// - /// Adds all children nodes of the current root to the current exclusion type's exclusion - /// rules cache. + /// Merges the current settings rules with the default settings rules. /// /// - /// The root node. - /// The exclusion rule. - private void AddNodes(XmlNode node, Exclusion exclusion) + /// The current settings root node. + /// The default settings root node. + private void MergeNodes(XmlNode currentNode, XmlNode defaultNode) { - foreach (XmlNode child in node.ChildNodes) - { - var key = child.OuterXml; + var exclusionCache = new HashSet(); - // Ignore keys that are already present in the current exclusion type's exclusion - // rules cache. - if (exclusion.ExclusionRules.ContainsKey(key)) - { - continue; - } - - // Add the current exclusion rule to the exclusion type's cache. - exclusion.ExclusionRules.Add(key, child); + // Add current exclusions to the exclusion cache. + foreach (XmlNode child in currentNode.ChildNodes) + { + exclusionCache.Add(child.OuterXml); } - } - /// - /// Merges the current settings rules with the default settings rules. - /// - /// - /// The root node. - /// The exclusion rule. - private void MergeNodes(XmlNode node, Exclusion exclusion) - { - // Iterate through all the children nodes of the given root. - foreach (XmlNode child in node.ChildNodes) + // Iterate through default exclusions and import missing ones. + foreach (XmlNode child in defaultNode.ChildNodes) { - if (!exclusion.ExclusionRules.ContainsKey(child.OuterXml)) + if (exclusionCache.Contains(child.OuterXml)) { continue; } - // Remove exclusion rule from the current exclusion type's cache. - exclusion.ExclusionRules.Remove(child.OuterXml); + // Import missing default exclusions. + var importedChild = currentNode.OwnerDocument.ImportNode(child, true); + currentNode.AppendChild(importedChild); } - - // Iterate through remaining items in the current exclusion type cache. - foreach (var child in exclusion.ExclusionRules.Values) - { - // Import any remaining items in the current settings document. - var importedChild = node.OwnerDocument.ImportNode(child, true); - node.AppendChild(importedChild); - } - - exclusion.ExclusionRules.Clear(); } #endregion } diff --git a/test/DataCollectors/TraceDataCollector.UnitTests/DynamicCoverageDataCollectorImplTests.cs b/test/DataCollectors/TraceDataCollector.UnitTests/DynamicCoverageDataCollectorImplTests.cs index cd0ff00f29..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; @@ -83,12 +84,10 @@ public void InitializeShouldCreateDefaultCodeCoverageSettingsIfConfigElementIsNu } [TestMethod] - [Ignore] public void InitializeShouldInitializeVanguardWithRightCoverageSettings() { - var expectedContent = "CoverageSettingsContent"; XmlElement configElement = - DynamicCoverageDataCollectorImplTests.CreateXmlElement($"{expectedContent}"); + DynamicCoverageDataCollectorImplTests.CreateXmlElement(@""); this.directoryHelperMock.Setup(d => d.CreateDirectory(It.IsAny())) .Callback((path) => @@ -102,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] @@ -375,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 index 3a2c254828..7968ccee59 100644 --- a/test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageRunSettingsProcessorTests.cs +++ b/test/Microsoft.TestPlatform.Utilities.UnitTests/CodeCoverageRunSettingsProcessorTests.cs @@ -5,6 +5,8 @@ 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; @@ -12,149 +14,21 @@ namespace Microsoft.TestPlatform.Utilities.UnitTests [TestClass] public class CodeCoverageRunSettingsProcessorTests { - private static readonly string DefaultSettings = 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$", - @" ", - @" ", - @" 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\\.*", - @" ", - @" ", - @" ", - @" ", - @" ", - @""); + #region Members + private XmlElement defaultSettings; private CodeCoverageRunSettingsProcessor processor; + #endregion - private XmlDocument document; - - 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); - } - + #region Constructors public CodeCoverageRunSettingsProcessorTests() { - this.processor = new CodeCoverageRunSettingsProcessor(); - this.document = new XmlDocument(); - - this.document.LoadXml(CodeCoverageRunSettingsProcessorTests.DefaultSettings); + this.defaultSettings = this.GetDefaultConfiguration(); + this.processor = new CodeCoverageRunSettingsProcessor(this.defaultSettings); } + #endregion + #region Test Methods [TestMethod] public void ProcessingShouldReturnNullForNullOrEmptySettings() { @@ -170,7 +44,7 @@ public void ProcessingShouldReturnNullForNullOrEmptySettings() public void MissingCodeCoverageTagShouldAddDefaultTag() { const string settings = ""; - string expected = $"{this.document.OuterXml}"; + string expected = $"{this.defaultSettings.OuterXml}"; Assert.AreEqual(expected, processor.Process(settings).OuterXml); } @@ -182,7 +56,7 @@ public void EmptyCodeCoverageTagShouldAddDefaultTag() var processedNode = processor.Process(settings); Assert.IsNotNull(processedNode); - var codeCoverageNodes = this.ExtractNodes(processedNode, this.document.DocumentElement, "./CodeCoverage"); + var codeCoverageNodes = this.ExtractNodes(processedNode, this.defaultSettings, "./CodeCoverage"); this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./ModulePaths/Exclude"); this.CompareResults(codeCoverageNodes.Item1, codeCoverageNodes.Item2, "./Functions/Exclude"); @@ -190,6 +64,40 @@ public void EmptyCodeCoverageTagShouldAddDefaultTag() 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() { @@ -304,5 +212,73 @@ public void MixedTestShouldCorrectlyAddMissingTags() 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 @@ + + + From fdd840fc2fb521b295355ad46e84f973591b79e3 Mon Sep 17 00:00:00 2001 From: Codrin Poienaru Date: Wed, 3 Jun 2020 17:56:47 +0200 Subject: [PATCH 15/15] Added exception handling --- .../DynamicCoverageDataCollectorImpl.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs b/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs index 78bd38dd42..40a8a9a38f 100644 --- a/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs +++ b/src/DataCollectors/TraceDataCollector/VanguardCollector/DynamicCoverageDataCollectorImpl.cs @@ -110,8 +110,22 @@ public virtual void Initialize( { var defaultConfigurationElement = DynamicCoverageDataCollectorImpl.GetDefaultConfiguration(); - var processor = new CodeCoverageRunSettingsProcessor(defaultConfigurationElement); - configurationElement = (XmlElement)processor.Process(configurationElement); + 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))