Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Reading traits/properties from xml test-run tree using cache #406

Merged
merged 17 commits into from
Oct 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NuGet.Config
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
<add key="AppVeyor NUnit CI Feed" value="https://ci.appveyor.com/nuget/nunit" />
<add key="AppVeyor NUnit Engine CI Feed" value="https://ci.appveyor.com/nuget/nunit-console" />
<add key="NUnit MyGet Feed" value="https://www.myget.org/F/nunit/api/v2" />
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>
7 changes: 6 additions & 1 deletion Osiris.Extended.ruleset
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Osiris.Extended" ToolsVersion="11.0">
<RuleSet Name="Osiris.Extended" ToolsVersion="15.0">
<Include Path="basiccorrectnessrules.ruleset" Action="Default" />
<Include Path="basicdesignguidelinerules.ruleset" Action="Default" />
<Include Path="extendeddesignguidelinerules.ruleset" Action="Default" />
<Include Path="globalizationrules.ruleset" Action="Default" />
<Include Path="minimumrecommendedrules.ruleset" Action="Default" />
<Rules AnalyzerId="CodeCracker.CSharp" RuleNamespace="CodeCracker.CSharp">
<Rule Id="CC0001" Action="None" />
<Rule Id="CC0097" Action="None" />
<Rule Id="CC0105" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
<Rule Id="CA1062" Action="None" />
<Rule Id="CA1704" Action="None" />
Expand Down
6 changes: 3 additions & 3 deletions src/NUnitTestAdapter/DumpXml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ public string RandomName()

public void AddTestEvent(string text)
{
txt.Append("<NUnitTestEvent>");
txt.Append("<NUnitTestEvent>\n");
txt.Append(text);
txt.Append("</NUnitTestEvent>");
txt.Append("\n</NUnitTestEvent>\n");
}


Expand All @@ -126,7 +126,7 @@ public static string AsString(this System.Xml.XmlNode node)
twriter.Formatting = System.Xml.Formatting.Indented;
twriter.Indentation = 3;
twriter.QuoteChar = '\'';
node.WriteContentTo(twriter);
node.WriteTo(twriter);
}
return swriter.ToString();
}
Expand Down
1 change: 0 additions & 1 deletion src/NUnitTestAdapter/NUnit3TestDiscoverer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ public void DiscoverTests(IEnumerable<string> sources, IDiscoveryContext discove
private int ProcessTestCases(XmlNode topNode, ITestCaseDiscoverySink discoverySink, TestConverter testConverter)
{
int cases = 0;

foreach (XmlNode testNode in topNode.SelectNodes("//test-case"))
{
try
Expand Down
3 changes: 1 addition & 2 deletions src/NUnitTestAdapter/NUnitEventListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ public void OnTestEvent(string report)
}
catch(Exception ex)
{
_recorder.SendMessage(TestMessageLevel.Warning,
string.Format("Error processing {0} event for {1}", node.Name, node.GetAttribute("fullname")));
_recorder.SendMessage(TestMessageLevel.Warning,$"Error processing {node.Name} event for {node.GetAttribute("fullname")}");
_recorder.SendMessage(TestMessageLevel.Warning, ex.ToString());
}
}
Expand Down
22 changes: 10 additions & 12 deletions src/NUnitTestAdapter/TestConverter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// ***********************************************************************
// Copyright (c) 2011-2015 Charlie Poole, Terje Sandstrom
// Copyright (c) 2011-2017 Charlie Poole, Terje Sandstrom
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
Expand All @@ -24,8 +24,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
Expand All @@ -38,8 +36,8 @@ public class TestConverter
private readonly TestLogger _logger;
private readonly Dictionary<string, TestCase> _vsTestCaseMap;
private readonly string _sourceAssembly;
private NavigationDataProvider _navigationDataProvider;
private bool _collectSourceInformation;
private readonly NavigationDataProvider _navigationDataProvider;
private readonly bool _collectSourceInformation;

#region Constructor

Expand All @@ -49,6 +47,7 @@ public TestConverter(TestLogger logger, string sourceAssembly, bool collectSourc
_sourceAssembly = sourceAssembly;
_vsTestCaseMap = new Dictionary<string, TestCase>();
_collectSourceInformation = collectSourceInformation;
TraitsCache = new Dictionary<string, List<Trait>>();

if (_collectSourceInformation)
{
Expand All @@ -58,8 +57,9 @@ public TestConverter(TestLogger logger, string sourceAssembly, bool collectSourc

#endregion

#region Public Methods
public IDictionary<string, List<Trait>> TraitsCache { get; }

#region Public Methods
/// <summary>
/// Converts an NUnit test into a TestCase for Visual Studio,
/// using the best method available according to the exact
Expand All @@ -68,7 +68,7 @@ public TestConverter(TestLogger logger, string sourceAssembly, bool collectSourc
public TestCase ConvertTestCase(XmlNode testNode)
{
if (testNode == null || testNode.Name != "test-case")
throw new ArgumentException("The argument must be a test case", "test");
throw new ArgumentException("The argument must be a test case", nameof(testNode));

// Return cached value if we have one
string id = testNode.GetAttribute("id");
Expand Down Expand Up @@ -118,11 +118,9 @@ public IList<VSTestResult> GetVSTestResults(XmlNode resultNode)

return results;
}

#endregion

#region Helper Methods

/// <summary>
/// Makes a TestCase from an NUnit test, adding
/// navigation data if it can be found.
Expand All @@ -131,7 +129,7 @@ private TestCase MakeTestCaseFromXmlNode(XmlNode testNode)
{
var testCase = new TestCase(
testNode.GetAttribute("fullname"),
new Uri(NUnit3TestExecutor.ExecutorUri),
new Uri(NUnitTestAdapter.ExecutorUri),
_sourceAssembly)
{
DisplayName = testNode.GetAttribute("name"),
Expand All @@ -151,7 +149,7 @@ private TestCase MakeTestCaseFromXmlNode(XmlNode testNode)
}
}

testCase.AddTraitsFromTestNode(testNode);
testCase.AddTraitsFromTestNode(testNode, TraitsCache);

return testCase;
}
Expand Down Expand Up @@ -218,7 +216,7 @@ private VSTestResult GetBasicResult(XmlNode resultNode)

/// <summary>
/// Looks for attachments in a results node and if any attachments are found they
/// are added to <paramref name="vsResult"/>
/// are returned"/>
/// </summary>
/// <param name="resultNode">xml node for test result</param>
/// <returns>attachments to be added to the test, it will be empty if no attachments are found</returns>
Expand Down
86 changes: 68 additions & 18 deletions src/NUnitTestAdapter/TraitsFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,61 +21,111 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;

namespace NUnit.VisualStudio.TestAdapter
{
public static class TraitsFeature
{

public static void AddTrait(this TestCase testCase, string name, string value)
{
if(testCase != null)
{
testCase.Traits.Add(new Trait(name, value));
}
testCase?.Traits.Add(new Trait(name, value));
}

public static void AddTraitsFromTestNode(this TestCase testCase, XmlNode test)
public static void AddTraitsFromTestNode(this TestCase testCase, XmlNode testNode, IDictionary<string,List<Trait>> traitsCache)
{
if (test.ParentNode != null)
AddTraitsFromTestNode(testCase, test.ParentNode);
var ancestor = testNode.ParentNode;
var key = ancestor.Attributes?["id"]?.Value;

foreach (XmlNode propertyNode in test.SelectNodes("properties/property"))
// Reading ancestor properties of a test-case node. And cacheing it.
while (ancestor != null && key != null)
{
if (traitsCache.ContainsKey(key))
{
testCase.Traits.AddRange(traitsCache[key]);
}
else
{
var nodesList = ancestor.SelectNodes("properties/property");
foreach (XmlNode propertyNode in nodesList)
{
string propertyName = propertyNode.GetAttribute("name");
string propertyValue = propertyNode.GetAttribute("value");

AddTraitsToCache(traitsCache, key, propertyName, propertyValue);
if (!IsInternalProperty(propertyName, propertyValue))
{
testCase.Traits.Add(new Trait(propertyName, propertyValue));
}
}
// Adding empty list to dictionary, so that we will not make SelectNodes call again.
if (nodesList.Count == 0 && !traitsCache.ContainsKey(key))
{
traitsCache[key] = new List<Trait>();
}
}
ancestor = ancestor.ParentNode;
key = ancestor?.Attributes?["id"]?.Value;
}

// No Need to store test-case properties in dictionary.
foreach (XmlNode propertyNode in testNode.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))
{
AddTrait(testCase, propertyName, propertyValue);
testCase.Traits.Add(new Trait(propertyName, propertyValue));
}
}
}

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);
}

private static void AddTraitsToCache(IDictionary<string, List<Trait>> traitsCache, string key, string propertyName, string propertyValue)
{
if (traitsCache.ContainsKey(key))
{
if(!IsInternalProperty(propertyName, propertyValue))
traitsCache[key].Add(new Trait(propertyName, propertyValue));
return;
}


var traits = new List<Trait>();

// Will add empty list of traits, if the property is internal type. So that we will not make SelectNodes call again.
if (!IsInternalProperty(propertyName, propertyValue))
{
traits.Add(new Trait(propertyName, propertyValue));
}
traitsCache[key] = traits;
}

public static IEnumerable<NTrait> GetTraits(this TestCase testCase)
{
var traits = new List<NTrait>();

if (testCase != null && testCase.Traits != null)
if (testCase?.Traits != null)
{
traits.AddRange(from trait in testCase.Traits let name = trait.Name let value = trait.Value select new NTrait(name, value));
}

return traits;
}
}

public class NTrait
{
public string Name { get; private set; }
public string Value { get; private set; }
public string Name { get; }
public string Value { get; }

public NTrait(string name, string value)
{
Expand Down
32 changes: 26 additions & 6 deletions src/NUnitTestAdapterTests/AdapterSettingsTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// ***********************************************************************
// Copyright (c) 2011-2017 Charlie Poole, Terje Sandstrom
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************

using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using NUnit.Framework;
using NUnit.VisualStudio.TestAdapter.Tests.Fakes;
Expand All @@ -16,7 +34,9 @@ public class AdapterSettingsTests
[SetUp]
public void SetUp()
{
_settings = new AdapterSettings(new TestLogger(new MessageLoggerStub(), 0));
var testlogger = new TestLogger(new MessageLoggerStub());
_settings = new AdapterSettings(testlogger);
testlogger.InitSettings(_settings);
}

[Test]
Expand Down
24 changes: 23 additions & 1 deletion src/NUnitTestAdapterTests/AsyncTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
using System;
// ***********************************************************************
// Copyright (c) 2011-2015 Charlie Poole, Terje Sandstrom
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************
using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using NUnit.Framework;
Expand Down
24 changes: 23 additions & 1 deletion src/NUnitTestAdapterTests/DumpXmlTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
using System.Text.RegularExpressions;
// ***********************************************************************
// Copyright (c) 2017 Charlie Poole, Terje Sandstrom
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************
using System.Text.RegularExpressions;
using NSubstitute;
using NUnit.Framework;
using NUnit.VisualStudio.TestAdapter.Dump;
Expand Down
Loading