Building the cache as we go, so that it will not break the compat.
Added a test to check the cache built.
MODIFY
src/NUnitTestAdapter/NUnit3TestDiscoverer.cs
src/NUnitTestAdapter/NUnit3TestDiscoverer.cs
@@ -158,7 +158,6 @@ namespace NUnit.VisualStudio.TestAdapter
private int ProcessTestCases(XmlNode topNode, ITestCaseDiscoverySink discoverySink, TestConverter testConverter)
{
int cases = 0;
- testConverter.BuildAttributesCache(topNode);
foreach (XmlNode testNode in topNode.SelectNodes("//test-case"))
{
try

MODIFY
src/NUnitTestAdapter/TestConverter.cs
src/NUnitTestAdapter/TestConverter.cs
@@ -49,6 +49,7 @@ namespace NUnit.VisualStudio.TestAdapter
_sourceAssembly = sourceAssembly;
_vsTestCaseMap = new Dictionary<string, TestCase>();
_collectSourceInformation = collectSourceInformation;
+ _attributesCache = new Dictionary<string, List<KeyValuePair<string, string>>>();
if (_collectSourceInformation)
{
@@ -58,8 +59,15 @@ namespace NUnit.VisualStudio.TestAdapter
#endregion
- #region Public Methods
+ public IDictionary<string, List<KeyValuePair<string, string>>> AttributesCache
+ {
+ get
+ {
+ return _attributesCache;
+ }
+ }
+ #region Public Methods
/// <summary>
/// Converts an NUnit test into a TestCase for Visual Studio,
/// using the best method available according to the exact
@@ -118,61 +126,9 @@ namespace NUnit.VisualStudio.TestAdapter
return results;
}
-
- /// <summary>
- /// Builds attributes cache for the testrun / assembly
- /// </summary>
- /// <param name="topNode"></param>
- public void BuildAttributesCache(XmlNode topNode)
- {
- var assemblyAttributes = topNode.SelectNodes("properties/property");
- this.ReadAttributes(assemblyAttributes);
-
- var testSuiteAttributes = topNode.SelectNodes("test-suite/test-suite/properties/property");
- this.ReadAttributes(testSuiteAttributes);
-
- var testCaseAttributes = topNode.SelectNodes("test-suite/test-suite/test-case/properties/property");
- this.ReadAttributes(testCaseAttributes);
-
- }
-
#endregion
#region Helper Methods
-
- private void ReadAttributes(XmlNodeList attributes)
- {
- var enumerator = attributes.GetEnumerator();
-
- while(enumerator.MoveNext())
- {
- var property = (XmlNode)enumerator.Current;
- var parentId = property.ParentNode?.ParentNode?.Attributes["id"]?.Value ?? "0-0000";
-
- var propertyName = property.Attributes["name"]?.Value;
- var propertyValue = property.Attributes["value"]?.Value;
-
- this.AddAttributesToCache(parentId, propertyName, propertyValue);
- }
- }
-
- private void AddAttributesToCache(string parentId, string propertyName, string propertyValue)
- {
- if (!string.IsNullOrEmpty(propertyName) && propertyName[0] != '_' && !string.IsNullOrEmpty(propertyValue))
- {
- if (_attributesCache.ContainsKey(parentId))
- {
- _attributesCache[parentId].Add(new KeyValuePair<string, string>(propertyName, propertyValue));
- }
- else
- {
- var kps = new List<KeyValuePair<string, string>>();
- kps.Add(new KeyValuePair<string, string>(propertyName, propertyValue));
- _attributesCache[parentId] = kps;
- }
- }
- }
-
/// <summary>
/// Makes a TestCase from an NUnit test, adding
/// navigation data if it can be found.

MODIFY
src/NUnitTestAdapter/TraitsFeature.cs
src/NUnitTestAdapter/TraitsFeature.cs
@@ -39,6 +39,7 @@ namespace NUnit.VisualStudio.TestAdapter
static TraitsFeature()
{
+ System.Diagnostics.Debugger.Launch();
TraitsProperty = typeof(TestCase).GetProperty("Traits");
if (TraitsProperty != null)
{
@@ -72,49 +73,85 @@ namespace NUnit.VisualStudio.TestAdapter
{
if (IsSupported)
{
- var assemblyId = testNode.ParentNode?.ParentNode?.Attributes["id"]?.Value;
- List<KeyValuePair<string, string>> testCaseProperties = new List<KeyValuePair<string, string>>();
-
- // Reading properties at the assembly level.
- if(!string.IsNullOrEmpty(assemblyId) && propertiesCache.ContainsKey(assemblyId))
- testCaseProperties = propertiesCache[assemblyId];
+ AddTraitsFromTestNode(testNode, TraitsProperty.GetValue(testCase, new object[0]), propertiesCache);
+ }
+ }
- // Reading properties at the class (test suite) level.
- var testSuiteId = testNode.ParentNode?.Attributes["id"]?.Value;
- if(!string.IsNullOrEmpty(testSuiteId) && propertiesCache.ContainsKey(testSuiteId))
- testCaseProperties.AddRange(propertiesCache[testSuiteId]);
+ private static bool IsInternalProperty(string propertyName, string propertyValue)
+ {
+ // Property names starting with '_' are for internal use only
+ return string.IsNullOrEmpty(propertyName) || propertyName[0] == '_' || string.IsNullOrEmpty(propertyValue);
+ }
- // Reading properties at the test case level.
- var testCaseId = testNode?.Attributes["id"].Value;
- if(!string.IsNullOrEmpty(testCaseId) && propertiesCache.ContainsKey(testCaseId))
- testCaseProperties.AddRange(propertiesCache[testCaseId]);
+ private static void AddTraitsFromTestNode(XmlNode test, object traitsCollection, IDictionary<string, List<KeyValuePair<string, string>>> propertiesCache)
+ {
+ var ancestor = test.ParentNode;
+ var key = ancestor.Attributes?["id"]?.Value;
- var obj = TraitsProperty.GetValue(testCase, new object[0]);
- foreach (var property in testCaseProperties)
+ // Reading ancestor properties of a test-case node. And cacheing it.
+ while (ancestor != null && key != null)
{
- TraitsCollectionAdd.Invoke(obj, new object[] { property.Key, property.Value });
- }
+ if(propertiesCache.ContainsKey(key))
+ {
+ foreach (var property in propertiesCache[key])
+ {
+ TraitsCollectionAdd.Invoke(traitsCollection, new object[] { property.Key, property.Value });
}
}
+ else
+ {
+ var nodesList = ancestor.SelectNodes("properties/property");
+ foreach (XmlNode propertyNode in nodesList)
+ {
+ string propertyName = propertyNode.GetAttribute("name");
+ string propertyValue = propertyNode.GetAttribute("value");
- private static void AddTraitsFromTestNode(XmlNode test, object traitsCollection)
+ AddAttributesToCache(propertiesCache, key, propertyName, propertyValue);
+ if (!IsInternalProperty(propertyName, propertyValue))
{
- if (test.ParentNode != null)
- AddTraitsFromTestNode(test.ParentNode, traitsCollection);
+ TraitsCollectionAdd.Invoke(traitsCollection, new object[] { propertyName, propertyValue });
+ }
+ }
+ // Adding empty list to dictionary, so that we will not make SelectNodes call again.
+ if(nodesList.Count == 0 && !propertiesCache.ContainsKey(key))
+ {
+ propertiesCache[key] = new List<KeyValuePair<string, string>>();
+ }
+ }
+ ancestor = ancestor.ParentNode;
+ key = ancestor?.Attributes?["id"]?.Value;
+ }
+ // No Need to store test-case properties in dictionary.
foreach (XmlNode propertyNode in test.SelectNodes("properties/property"))
{
string propertyName = propertyNode.GetAttribute("name");
string propertyValue = propertyNode.GetAttribute("value");
- // Property names starting with '_' are for internal use only
- if (!string.IsNullOrEmpty(propertyName) && propertyName[0] != '_' && !string.IsNullOrEmpty(propertyValue))
+ if (!IsInternalProperty(propertyName, propertyValue))
{
TraitsCollectionAdd.Invoke(traitsCollection, new object[] { propertyName, propertyValue });
}
}
}
+ private static void AddAttributesToCache(IDictionary<string, List<KeyValuePair<string, string>>> propertiesCache, string key, string propertyName, string propertyValue)
+ {
+ if (propertiesCache.ContainsKey(key) && !IsInternalProperty(propertyName, propertyValue))
+ {
+ propertiesCache[key].Add(new KeyValuePair<string, string>(propertyName, propertyValue));
+ }
+ else
+ {
+ var kps = new List<KeyValuePair<string, string>>();
+ if (!IsInternalProperty(propertyName, propertyValue))
+ {
+ kps.Add(new KeyValuePair<string, string>(propertyName, propertyValue));
+ }
+ propertiesCache[key] = kps;
+ }
+ }
+
public static IEnumerable<NTrait> GetTraits(this TestCase testCase)
{
var traits = new List<NTrait>();

MODIFY
src/NUnitTestAdapterTests/TestConverterTests.cs
src/NUnitTestAdapterTests/TestConverterTests.cs
@@ -52,8 +52,20 @@ namespace NUnit.VisualStudio.TestAdapter.Tests
public void CanMakeTestCaseFromTest()
{
var testCase = testConverter.ConvertTestCase(fakeTestNode);
+ CheckTestCase(testCase);
+ }
+
+ [Test]
+ public void CanMakeTestCaseFromTestWithCache()
+ {
+
+ var testCase = testConverter.ConvertTestCase(fakeTestNode);
CheckTestCase(testCase);
+ if(TraitsFeature.IsSupported)
+ {
+ Assert.That(testConverter.AttributesCache.Keys.Count, Is.EqualTo(2));
+ }
}
[Test]

Building the cache as we go, so that it will not break the compat.
Added a test to check the cache built.