Skip to content

Commit

Permalink
Updated with testcase xml
Browse files Browse the repository at this point in the history
  • Loading branch information
Terje Sandstrom committed Jan 22, 2020
1 parent 915a25a commit 5c9bd67
Show file tree
Hide file tree
Showing 13 changed files with 262 additions and 83 deletions.
37 changes: 11 additions & 26 deletions src/NUnitTestAdapter/NUnit3TestDiscoverer.cs
Expand Up @@ -35,6 +35,7 @@
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using NUnit.Engine;
using NUnit.VisualStudio.TestAdapter.Dump;
using NUnit.VisualStudio.TestAdapter.NUnitEngine;

namespace NUnit.VisualStudio.TestAdapter
{
Expand Down Expand Up @@ -79,7 +80,7 @@ public void DiscoverTests(IEnumerable<string> sources, IDiscoveryContext discove
{
string sourceAssemblyPath = Path.IsPathRooted(sourceAssembly) ? sourceAssembly : Path.Combine(Directory.GetCurrentDirectory(), sourceAssembly);
TestLog.Debug("Processing " + sourceAssembly);
ITestRunner runner = null;
INUnitEngineAdapter runner = null;

if (Settings.DumpXmlTestDiscovery)
{
Expand All @@ -89,20 +90,15 @@ public void DiscoverTests(IEnumerable<string> sources, IDiscoveryContext discove
try
{
runner = GetRunnerFor(sourceAssemblyPath, null);
var topNode = runner.Explore(TestFilter.Empty);
dumpXml?.AddString(topNode.AsString());
var results = runner.Explore();
dumpXml?.AddString(results.AsString());

// Currently, this will always be the case but it might change
if (topNode.Name == "test-run")
topNode = topNode.FirstChild;

// ReSharper disable once StringLiteralTypo
if (topNode.GetAttribute("runstate") == "Runnable")
if (results.IsRunnable)
{
int cases;
using (var testConverter = new TestConverter(TestLog, sourceAssemblyPath, Settings))
{
cases = ProcessTestCases(topNode, discoverySink, testConverter);
cases = ProcessTestCases(results, discoverySink, testConverter);
}

TestLog.Debug($"Discovered {cases} test cases");
Expand All @@ -114,10 +110,7 @@ public void DiscoverTests(IEnumerable<string> sources, IDiscoveryContext discove
}
else
{
var msgNode = topNode.SelectSingleNode("properties/property[@name='_SKIPREASON']");
if (msgNode != null &&
(new[] { "contains no tests", "Has no TestFixtures" }).Any(msgNode.GetAttribute("value")
.Contains))
if (results.HasNoNUnitTests)
{
if (Settings.Verbosity > 0)
TestLog.Info("Assembly contains no NUnit 3.0 tests: " + sourceAssembly);
Expand Down Expand Up @@ -170,15 +163,7 @@ public void DiscoverTests(IEnumerable<string> sources, IDiscoveryContext discove
finally
{
dumpXml?.Dump4Discovery();

if (runner != null)
{
if (runner.IsTestRunning)
runner.StopRun(true);

runner.Unload();
runner.Dispose();
}
runner?.Close();
}
}

Expand All @@ -191,18 +176,18 @@ public void DiscoverTests(IEnumerable<string> sources, IDiscoveryContext discove

#region Helper Methods

private int ProcessTestCases(XmlNode topNode, ITestCaseDiscoverySink discoverySink, TestConverter testConverter)
private int ProcessTestCases(NUnitResults results, ITestCaseDiscoverySink discoverySink, TestConverter testConverter)
{
int cases = 0;
foreach (XmlNode testNode in topNode.SelectNodes("//test-case"))
foreach (XmlNode testNode in results.TestCases())
{
try
{
#if LAUNCHDEBUGGER
if (!Debugger.IsAttached)
Debugger.Launch();
#endif
TestCase testCase = testConverter.ConvertTestCase(testNode);
var testCase = testConverter.ConvertTestCase(new NUnitTestCase(testNode));
discoverySink.SendTestCase(testCase);
cases += 1;
}
Expand Down
58 changes: 27 additions & 31 deletions src/NUnitTestAdapter/NUnit3TestExecutor.cs
Expand Up @@ -35,6 +35,7 @@
using NUnit.Engine;
using NUnit.Engine.Services;
using NUnit.VisualStudio.TestAdapter.Dump;
using NUnit.VisualStudio.TestAdapter.NUnitEngine;

namespace NUnit.VisualStudio.TestAdapter
{
Expand All @@ -47,7 +48,8 @@ public NUnit3TestExecutor()
}

// Fields related to the currently executing assembly
private ITestRunner _activeRunner;
// private ITestRunner _activeRunner;
private INUnitEngineAdapter nunitEngineAdapter;

#region Properties

Expand Down Expand Up @@ -94,11 +96,11 @@ public void RunTests(IEnumerable<string> sources, IRunContext runContext, IFrame
return;
}

foreach (var assemblyName in sources)
foreach (string assemblyName in sources)
{
try
{
var assemblyPath = Path.IsPathRooted(assemblyName) ? assemblyName : Path.Combine(Directory.GetCurrentDirectory(), assemblyName);
string assemblyPath = Path.IsPathRooted(assemblyName) ? assemblyName : Path.Combine(Directory.GetCurrentDirectory(), assemblyName);
var filter = CreateTestFilterBuilder().FilterByWhere(Settings.Where);

RunAssembly(assemblyPath, null, filter);
Expand Down Expand Up @@ -142,8 +144,8 @@ public void RunTests(IEnumerable<TestCase> tests, IRunContext runContext, IFrame
{
try
{
var assemblyName = assemblyGroup.Key;
var assemblyPath = Path.IsPathRooted(assemblyName) ? assemblyName : Path.Combine(Directory.GetCurrentDirectory(), assemblyName);
string assemblyName = assemblyGroup.Key;
string assemblyPath = Path.IsPathRooted(assemblyName) ? assemblyName : Path.Combine(Directory.GetCurrentDirectory(), assemblyName);

var filterBuilder = CreateTestFilterBuilder();
var filter = filterBuilder.FilterByList(assemblyGroup);
Expand All @@ -157,13 +159,14 @@ public void RunTests(IEnumerable<TestCase> tests, IRunContext runContext, IFrame
}
}

TestLog.Info(string.Format("NUnit Adapter {0}: Test execution complete", AdapterVersion));
TestLog.Info($"NUnit Adapter {AdapterVersion}: Test execution complete");
Unload();
}

void ITestExecutor.Cancel()
{
_activeRunner?.StopRun(true);
// _activeRunner?.StopRun(true);
nunitEngineAdapter?.StopRun();
}

#endregion
Expand Down Expand Up @@ -205,7 +208,7 @@ public void InitializeForExecution(IRunContext runContext, IFrameworkHandle fram
frameworkHandle.EnableShutdownAfterTestRun = enableShutdown;
}

TestLog.Debug("EnableShutdown: " + enableShutdown.ToString());
TestLog.Debug("EnableShutdown: " + enableShutdown);
}

private void RunAssembly(string assemblyPath, IGrouping<string, TestCase> testCases, TestFilter filter)
Expand All @@ -215,7 +218,7 @@ private void RunAssembly(string assemblyPath, IGrouping<string, TestCase> testCa
Debugger.Launch();
#endif

var actionText = Debugger.IsAttached ? "Debugging " : "Running ";
string actionText = Debugger.IsAttached ? "Debugging " : "Running ";
string selectionText = filter == null || filter == TestFilter.Empty ? "all" : "selected";
TestLog.Info(actionText + selectionText + " tests in " + assemblyPath);

Expand All @@ -230,17 +233,14 @@ private void RunAssembly(string assemblyPath, IGrouping<string, TestCase> testCa

try
{
_activeRunner = GetRunnerFor(assemblyPath, testCases);
nunitEngineAdapter = GetRunnerFor(assemblyPath, testCases);
CreateTestOutputFolder();
var loadResult = _activeRunner.Explore(filter);
dumpXml?.AddString(loadResult.AsString());
if (loadResult.Name == "test-run")
loadResult = loadResult.FirstChild;
var discoveryResults = nunitEngineAdapter.Explore(filter); // _activeRunner.Explore(filter);
dumpXml?.AddString(discoveryResults.AsString());

// ReSharper disable once StringLiteralTypo
if (loadResult.GetAttribute("runstate") == "Runnable")
if (discoveryResults.IsRunnable)
{
var nunitTestCases = loadResult.SelectNodes("//test-case");
var nunitTestCases = discoveryResults.TestCases();

using (var testConverter = new TestConverter(TestLog, assemblyPath, Settings))
{
Expand All @@ -250,11 +250,10 @@ private void RunAssembly(string assemblyPath, IGrouping<string, TestCase> testCa
// the converter's cache of all test cases is populated as well.
// All future calls to convert a test case may now use the cache.
foreach (XmlNode testNode in nunitTestCases)
loadedTestCases.Add(testConverter.ConvertTestCase(testNode));
loadedTestCases.Add(testConverter.ConvertTestCase(new NUnitTestCase(testNode)));


TestLog.Info(
$" NUnit3TestExecutor converted {loadedTestCases.Count} of {nunitTestCases.Count} NUnit test cases");
TestLog.Info($" NUnit3TestExecutor converted {loadedTestCases.Count} of {nunitTestCases.Count} NUnit test cases");


// If we have a TFS Filter, convert it to an nunit filter
Expand All @@ -275,8 +274,8 @@ private void RunAssembly(string assemblyPath, IGrouping<string, TestCase> testCa
{
try
{
var results = _activeRunner.Run(listener, filter);
GenerateTestOutput(results, assemblyPath);
var results = nunitEngineAdapter.Run(listener, filter);
GenerateTestOutput(results.TopNode, assemblyPath);
}
catch (NullReferenceException)
{
Expand All @@ -288,11 +287,9 @@ private void RunAssembly(string assemblyPath, IGrouping<string, TestCase> testCa
}
else
{
var msgNode = loadResult.SelectSingleNode("properties/property[@name='_SKIPREASON']");
if (msgNode != null && new[] { "contains no tests", "Has no TestFixtures" }.Any(msgNode.GetAttribute("value").Contains))
TestLog.Info(" NUnit couldn't find any tests in " + assemblyPath);
else
TestLog.Info(" NUnit failed to load " + assemblyPath);
TestLog.Info(discoveryResults.HasNoNUnitTests
? " NUnit couldn't find any tests in " + assemblyPath
: " NUnit failed to load " + assemblyPath);
}
}
catch (BadImageFormatException)
Expand Down Expand Up @@ -325,8 +322,7 @@ private void RunAssembly(string assemblyPath, IGrouping<string, TestCase> testCa
dumpXml?.Dump4Execution();
try
{
_activeRunner?.Dispose();
_activeRunner = null;
nunitEngineAdapter?.Close();
}
catch (Exception ex)
{
Expand All @@ -344,7 +340,7 @@ private void GenerateTestOutput(XmlNode testResults, string assemblyPath)
if (!Settings.UseTestOutputXml)
return;

var path = Path.Combine(TestOutputXmlFolder, $"{Path.GetFileNameWithoutExtension(assemblyPath)}.xml");
string path = Path.Combine(TestOutputXmlFolder, $"{Path.GetFileNameWithoutExtension(assemblyPath)}.xml");
#if NET35
var resultService = TestEngine.Services.GetService<IResultService>();
#else
Expand Down Expand Up @@ -374,7 +370,7 @@ private void CreateTestOutputFolder()
return;
}

var path = Path.IsPathRooted(Settings.TestOutputXml)
string path = Path.IsPathRooted(Settings.TestOutputXml)
? Settings.TestOutputXml
: Path.Combine(WorkDir, Settings.TestOutputXml);
try
Expand Down
104 changes: 104 additions & 0 deletions src/NUnitTestAdapter/NUnitEngine/NUnitEngineAdapter.cs
@@ -0,0 +1,104 @@
// ***********************************************************************
// Copyright (c) 2020-2020 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 NUnit.Engine;
// We use an alias so that we don't accidentally make
// references to engine internals, except for creating
// the engine object in the Initialize method.
using TestEngineClass = NUnit.Engine.TestEngine;

namespace NUnit.VisualStudio.TestAdapter.NUnitEngine
{
public interface INUnitEngineAdapter
{
NUnitResults Explore();
void Close();
NUnitResults Explore(TestFilter filter);
NUnitResults Run(ITestEventListener listener, TestFilter filter);
void StopRun();
}

public class NUnitEngineAdapter : INUnitEngineAdapter, IDisposable
{
private readonly IAdapterSettings settings;
private readonly ITestLogger logger;
private readonly TestPackage package;
private TestEngineClass TestEngine { get; }
private readonly ITestRunner runner;

internal event Action<TestEngineClass> InternalEngineCreated;

private NUnitEngineAdapter(IAdapterSettings settings, ITestLogger logger)
{
this.logger = logger;
this.settings = settings;
}

public NUnitEngineAdapter(TestPackage package, IAdapterSettings settings, ITestLogger testLog) : this(settings, testLog)
{
this.package = package;
var engine = new TestEngineClass();
InternalEngineCreated?.Invoke(engine);
TestEngine = engine;
runner = TestEngine.GetRunner(package);
}

public NUnitResults Explore()
{
return new NUnitResults(runner.Explore(TestFilter.Empty));
}

public NUnitResults Explore(TestFilter filter)
{
return new NUnitResults(runner.Explore(filter));
}

public NUnitResults Run(ITestEventListener listener, TestFilter filter)
{
return new NUnitResults(runner.Run(listener, filter));
}

public void StopRun()
{
runner?.StopRun(true);
}

public void Close()
{
if (runner == null)
return;
if (runner.IsTestRunning)
runner.StopRun(true);

runner.Unload();
runner.Dispose();
}

public void Dispose()
{
Close();
TestEngine?.Dispose();
}
}
}

0 comments on commit 5c9bd67

Please sign in to comment.