diff --git a/.gitignore b/.gitignore index de69fc2e0..8bb1565a6 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.suo *.user *.sln.docstates +*.project.lock.json # Build results diff --git a/.travis.yml b/.travis.yml index 593e92bd8..c1eb5b7d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,18 @@ language: csharp -sudo: false -mono: - - latest - - 4.2.4 -os: - - linux - - osx +sudo: required matrix: + include: + - os: linux + dist: trusty + mono: latest + - os: linux + dist: trusty + mono: 4.2.4 + - os: osx + mono: latest allow_failures: - mono: latest fast_finish: true + script: - ./build.sh --target "Travis" diff --git a/NUnit.Engine.Netstandard.sln b/NUnit.Engine.Netstandard.sln new file mode 100644 index 000000000..f45cb3632 --- /dev/null +++ b/NUnit.Engine.Netstandard.sln @@ -0,0 +1,58 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.10 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{49D441DF-39FD-4F4D-AECA-86CF8EFE23AF}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitattributes = .gitattributes + .gitignore = .gitignore + .travis.yml = .travis.yml + appveyor.yml = appveyor.yml + build.cake = build.cake + BUILDING.md = BUILDING.md + CHANGES.txt = CHANGES.txt + CONTRIBUTING.md = CONTRIBUTING.md + LICENSE.txt = LICENSE.txt + NOTICES.txt = NOTICES.txt + NuGet.config = NuGet.config + nunit.ico = nunit.ico + README.md = README.md + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nunit.engine.tests.netstandard", "src\NUnitEngine\nunit.engine.tests.netstandard\nunit.engine.tests.netstandard.csproj", "{BC22F0E4-0862-4F82-A912-C9A447FECBAD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mock-assembly.netstandard", "src\NUnitEngine\mock-assembly\mock-assembly.netstandard.csproj", "{BC6C8155-2024-4F58-A006-18A15EA22A5C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nunit.engine.netstandard", "src\NUnitEngine\nunit.engine.netstandard\nunit.engine.netstandard.csproj", "{C26FFC46-60CE-4CBA-87BD-8E9B972C0E0D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BC22F0E4-0862-4F82-A912-C9A447FECBAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC22F0E4-0862-4F82-A912-C9A447FECBAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC22F0E4-0862-4F82-A912-C9A447FECBAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC22F0E4-0862-4F82-A912-C9A447FECBAD}.Release|Any CPU.Build.0 = Release|Any CPU + {BC6C8155-2024-4F58-A006-18A15EA22A5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC6C8155-2024-4F58-A006-18A15EA22A5C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC6C8155-2024-4F58-A006-18A15EA22A5C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC6C8155-2024-4F58-A006-18A15EA22A5C}.Release|Any CPU.Build.0 = Release|Any CPU + {C26FFC46-60CE-4CBA-87BD-8E9B972C0E0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C26FFC46-60CE-4CBA-87BD-8E9B972C0E0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C26FFC46-60CE-4CBA-87BD-8E9B972C0E0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C26FFC46-60CE-4CBA-87BD-8E9B972C0E0D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = src\NUnitFramework\tests\nunitlite.tests-2.0.csproj + {28B605B2-E2E9-417E-8369-49E263F1F31B} = {31B45C4C-206F-4F31-9CC6-33BF11DFEE39} + {FFF45826-991F-465B-8A03-0E1DB7E8F38C} = {31B45C4C-206F-4F31-9CC6-33BF11DFEE39} + {11640C9F-03A3-456B-848D-9B4A126F9506} = {31B45C4C-206F-4F31-9CC6-33BF11DFEE39} + EndGlobalSection +EndGlobal diff --git a/build.cake b/build.cake index fdfb4e840..90bd64185 100644 --- a/build.cake +++ b/build.cake @@ -16,7 +16,7 @@ var ErrorDetail = new List(); ////////////////////////////////////////////////////////////////////// var version = "3.7.0"; -var modifier = ""; +var modifier = "-alpha1"; var isAppveyor = BuildSystem.IsRunningOnAppVeyor; var dbgSuffix = configuration == "Debug" ? "-dbg" : ""; @@ -32,6 +32,8 @@ var BIN_DIR = PROJECT_DIR + "bin/" + configuration + "/"; var IMAGE_DIR = PROJECT_DIR + "images/"; var SOLUTION_FILE = "NUnitConsole.sln"; +var DOTNETCORE_SOLUTION_FILE = "NUnit.Engine.NetStandard.sln"; +var DOTNETCORE_TEST_ASSEMBLY = "src/NUnitEngine/nunit.engine.tests.netstandard/bin/" + configuration + "/netcoreapp1.1/nunit.engine.tests.netstandard.dll"; // Package sources for nuget restore var PACKAGE_SOURCE = new string[] @@ -47,6 +49,24 @@ var NUNIT3_CONSOLE = BIN_DIR + "nunit3-console.exe"; var ENGINE_TESTS = "nunit.engine.tests.dll"; var CONSOLE_TESTS = "nunit3-console.tests.dll"; +bool IsDotNetCoreInstalled = false; + +////////////////////////////////////////////////////////////////////// +// SETUP AND TEARDOWN TASKS +////////////////////////////////////////////////////////////////////// +Setup(context => +{ + // Executed BEFORE the first task. + Information("Building version {0} of NUnit.", packageVersion); + IsDotNetCoreInstalled = CheckIfDotNetCoreInstalled(); +}); + +Teardown(context => +{ + // Executed AFTER the last task. + CheckForError(ref ErrorDetail); +}); + ////////////////////////////////////////////////////////////////////// // CLEAN ////////////////////////////////////////////////////////////////////// @@ -67,10 +87,18 @@ Task("InitializeBuild") .Description("Initializes the build") .Does(() => { - NuGetRestore(SOLUTION_FILE, new NuGetRestoreSettings() - { - Source = PACKAGE_SOURCE - }); + Information("Restoring NuGet packages"); + NuGetRestore(SOLUTION_FILE, new NuGetRestoreSettings + { + Source = PACKAGE_SOURCE, + Verbosity = NuGetVerbosity.Detailed + }); + + if(IsDotNetCoreInstalled && IsRunningOnWindows()) + { + Information("Restoring .NET Core packages"); + DotNetCoreRestore(DOTNETCORE_SOLUTION_FILE); + } if (BuildSystem.IsRunningOnAppVeyor) { @@ -133,6 +161,32 @@ Task("BuildEngine") BuildProject("./src/NUnitEngine/notest-assembly/notest-assembly.csproj", configuration); }); +////////////////////////////////////////////////////////////////////// +// BUILD NETSTANDARD ENGINE +////////////////////////////////////////////////////////////////////// + +Task("BuildNetStandardEngine") + .Description("Builds the .NET Standard engine") + .IsDependentOn("InitializeBuild") + .WithCriteria(IsRunningOnWindows()) + .Does(() => + { + if(IsDotNetCoreInstalled) + { + var settings = new DotNetCoreBuildSettings + { + Configuration = configuration, + EnvironmentVariables = new Dictionary() + }; + settings.EnvironmentVariables.Add("PackageVersion", packageVersion); + DotNetCoreBuild(DOTNETCORE_SOLUTION_FILE, settings); + } + else + { + Warning("Skipping .NET Standard build because .NET Core is not installed"); + } + }); + ////////////////////////////////////////////////////////////////////// // BUILD CONSOLE ////////////////////////////////////////////////////////////////////// @@ -204,6 +258,27 @@ Task("TestConsole") RunTest(NUNIT3_CONSOLE, BIN_DIR, CONSOLE_TESTS, "TestConsole", ref ErrorDetail); }); +////////////////////////////////////////////////////////////////////// +// TEST NETSTANDARD ENGINE +////////////////////////////////////////////////////////////////////// + +Task("TestNetStandardEngine") + .Description("Tests the .NET Standard Engine") + .IsDependentOn("BuildNetStandardEngine") + .WithCriteria(IsRunningOnWindows()) + .OnError(exception => { ErrorDetail.Add(exception.Message); }) + .Does(() => + { + if(IsDotNetCoreInstalled) + { + DotNetCoreExecute(DOTNETCORE_TEST_ASSEMBLY); + } + else + { + Warning("Skipping .NET Standard tests because .NET Core is not installed"); + } + }); + ////////////////////////////////////////////////////////////////////// // PACKAGE ////////////////////////////////////////////////////////////////////// @@ -337,23 +412,47 @@ Task("PackageConsole") }); ////////////////////////////////////////////////////////////////////// -// SETUP AND TEARDOWN TASKS +// PACKAGE NETSTANDARD ENGINE ////////////////////////////////////////////////////////////////////// -Setup(context => -{ - // Executed BEFORE the first task. -}); -Teardown(context => -{ - // Executed AFTER the last task. - CheckForError(ref ErrorDetail); -}); +Task("PackageNetStandardEngine") + .Description("Copies the .NET Standard Engine nuget package in the packages directory") + .WithCriteria(IsRunningOnWindows()) + .Does(() => + { + if(IsDotNetCoreInstalled) + { + var nuget = "nunit.engine.netstandard." + packageVersion + ".nupkg"; + var src = "src/NUnitEngine/nunit.engine.netstandard/bin/" + configuration + "/" + nuget; + var dest = PACKAGE_DIR + nuget; + + CreateDirectory(PACKAGE_DIR); + CopyFile(src, dest); + } + }); ////////////////////////////////////////////////////////////////////// // HELPER METHODS - GENERAL ////////////////////////////////////////////////////////////////////// +bool CheckIfDotNetCoreInstalled() +{ + try + { + Information("Checking if .NET Core SDK is installed"); + StartProcess("dotnet", new ProcessSettings + { + Arguments = "--version" + }); + } + catch(Exception) + { + Warning(".NET Core SDK is not installed. It can be installed from https://www.microsoft.com/net/core"); + return false; + } + return true; +} + void RunGitCommand(string arguments) { StartProcess("git", new ProcessSettings() @@ -445,7 +544,8 @@ void RunTest(FilePath exePath, DirectoryPath workingDir, string arguments, strin Task("Build") .Description("Builds the engine and console runner") .IsDependentOn("BuildEngine") - .IsDependentOn("BuildConsole"); + .IsDependentOn("BuildConsole") + .IsDependentOn("BuildNetStandardEngine"); Task("Rebuild") .Description("Rebuilds the engine and console runner") @@ -455,13 +555,15 @@ Task("Rebuild") Task("Test") .Description("Builds and tests the engine and console runner") .IsDependentOn("TestEngine") - .IsDependentOn("TestConsole"); + .IsDependentOn("TestConsole") + .IsDependentOn("TestNetStandardEngine"); Task("Package") .Description("Packages the engine and console runner") .IsDependentOn("CheckForError") .IsDependentOn("PackageEngine") - .IsDependentOn("PackageConsole"); + .IsDependentOn("PackageConsole") + .IsDependentOn("PackageNetStandardEngine"); Task("Appveyor") .Description("Builds, tests and packages on AppVeyor") diff --git a/src/CommonAssemblyInfo.cs b/src/CommonAssemblyInfo.cs index b5bea5826..6af70b918 100644 --- a/src/CommonAssemblyInfo.cs +++ b/src/CommonAssemblyInfo.cs @@ -42,16 +42,10 @@ [assembly: AssemblyConfiguration(".NET 4.0 Debug")] #elif NET_2_0 [assembly: AssemblyConfiguration(".NET 2.0 Debug")] -#elif SL_5_0 -[assembly: AssemblyConfiguration("Silverlight 5.0 Debug")] -#elif SL_4_0 -[assembly: AssemblyConfiguration("Silverlight 4.0 Debug")] -#elif SL_3_0 -[assembly: AssemblyConfiguration("Silverlight 3.0 Debug")] -#elif NETCF_3_5 -[assembly: AssemblyConfiguration("Compact Framework 3.5 Debug")] #elif PORTABLE [assembly: AssemblyConfiguration("Portable Debug")] +#elif NETSTANDARD1_3 || NETSTANDARD1_6 || NETCOREAPP1_0 +[assembly: AssemblyConfiguration(".NET Standard Debug")] #else [assembly: AssemblyConfiguration("Debug")] #endif @@ -62,16 +56,10 @@ [assembly: AssemblyConfiguration(".NET 4.0")] #elif NET_2_0 [assembly: AssemblyConfiguration(".NET 2.0")] -#elif SL_5_0 -[assembly: AssemblyConfiguration("Silverlight 5.0")] -#elif SL_4_0 -[assembly: AssemblyConfiguration("Silverlight 4.0")] -#elif SL_3_0 -[assembly: AssemblyConfiguration("Silverlight 3.0")] -#elif NETCF_3_5 -[assembly: AssemblyConfiguration("Compact Framework 3.5")] #elif PORTABLE [assembly: AssemblyConfiguration("Portable")] +#elif NETSTANDARD1_3 || NETSTANDARD1_6 || NETCOREAPP1_0 +[assembly: AssemblyConfiguration(".NET Standard")] #else [assembly: AssemblyConfiguration("")] #endif diff --git a/src/NUnitEngine/mock-assembly/MockAssembly.cs b/src/NUnitEngine/mock-assembly/MockAssembly.cs index 02159c6af..3105ab104 100644 --- a/src/NUnitEngine/mock-assembly/MockAssembly.cs +++ b/src/NUnitEngine/mock-assembly/MockAssembly.cs @@ -99,7 +99,9 @@ public class MockAssembly public const int Inconclusive = MockTestFixture.Inconclusive; +#if !NETSTANDARD1_6 public static readonly string AssemblyPath = AssemblyHelper.GetAssemblyPath(typeof(MockAssembly).Assembly); +#endif } [TestFixture(Description="Fake Test Fixture")] diff --git a/src/NUnitEngine/mock-assembly/Properties/AssemblyInfo.cs b/src/NUnitEngine/mock-assembly/Properties/AssemblyInfo.cs index 4b5aa9eac..4dc235de1 100644 --- a/src/NUnitEngine/mock-assembly/Properties/AssemblyInfo.cs +++ b/src/NUnitEngine/mock-assembly/Properties/AssemblyInfo.cs @@ -28,4 +28,4 @@ [assembly: AssemblyTitle("mock-assembly")] [assembly: AssemblyDescription("")] -[assembly: AssemblyCulture("")] +[assembly: AssemblyCulture("")] \ No newline at end of file diff --git a/src/NUnitEngine/mock-assembly/mock-assembly.netstandard.csproj b/src/NUnitEngine/mock-assembly/mock-assembly.netstandard.csproj new file mode 100644 index 000000000..d5c579962 --- /dev/null +++ b/src/NUnitEngine/mock-assembly/mock-assembly.netstandard.csproj @@ -0,0 +1,29 @@ + + + NUnit.Tests + netstandard1.6 + mock-assembly + + + true + + + ..\..\nunit.snk + false + $(PackageVersion) + + + + + + + + + + + + + nunit.snk + + + \ No newline at end of file diff --git a/src/NUnitEngine/nunit.engine.api/Extensibility/IFrameworkDriver.cs b/src/NUnitEngine/nunit.engine.api/Extensibility/IFrameworkDriver.cs index 8ab4bbab5..be1f9d8c2 100644 --- a/src/NUnitEngine/nunit.engine.api/Extensibility/IFrameworkDriver.cs +++ b/src/NUnitEngine/nunit.engine.api/Extensibility/IFrameworkDriver.cs @@ -8,10 +8,10 @@ // 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 @@ -23,6 +23,7 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Xml; namespace NUnit.Engine.Extensibility diff --git a/src/NUnitEngine/nunit.engine.api/ITestEngine.cs b/src/NUnitEngine/nunit.engine.api/ITestEngine.cs index e582d5c45..ba8177dd7 100644 --- a/src/NUnitEngine/nunit.engine.api/ITestEngine.cs +++ b/src/NUnitEngine/nunit.engine.api/ITestEngine.cs @@ -34,11 +34,13 @@ namespace NUnit.Engine /// public interface ITestEngine : IDisposable { +#if !NETSTANDARD1_3 /// /// Gets the IServiceLocator interface, which gives access to /// certain services provided by the engine. /// IServiceLocator Services { get; } +#endif /// /// Gets and sets the directory path used by the engine for saving files. diff --git a/src/NUnitEngine/nunit.engine.api/ITestRunner.cs b/src/NUnitEngine/nunit.engine.api/ITestRunner.cs index e9ca83bb2..b7c39a44e 100644 --- a/src/NUnitEngine/nunit.engine.api/ITestRunner.cs +++ b/src/NUnitEngine/nunit.engine.api/ITestRunner.cs @@ -77,6 +77,7 @@ public interface ITestRunner : IDisposable /// An XmlNode giving the result of the test execution XmlNode Run(ITestEventListener listener, TestFilter filter); +#if !NETSTANDARD1_3 /// /// Start a run of the tests in the loaded TestPackage. The tests are run /// asynchronously and the listener interface is notified as it progresses. @@ -85,6 +86,7 @@ public interface ITestRunner : IDisposable /// A TestFilter used to select tests /// ITestRun RunAsync(ITestEventListener listener, TestFilter filter); +#endif /// /// Cancel the ongoing test run. If no test is running, the call is ignored. diff --git a/src/NUnitEngine/nunit.engine.api/NUnitEngineException.cs b/src/NUnitEngine/nunit.engine.api/NUnitEngineException.cs index 83f51b353..d6b81b635 100644 --- a/src/NUnitEngine/nunit.engine.api/NUnitEngineException.cs +++ b/src/NUnitEngine/nunit.engine.api/NUnitEngineException.cs @@ -22,7 +22,9 @@ // *********************************************************************** using System; +#if !NETSTANDARD1_3 using System.Runtime.Serialization; +#endif namespace NUnit.Engine { @@ -31,7 +33,9 @@ namespace NUnit.Engine /// called with improper values or when a particular facility /// is not available. /// +#if !NETSTANDARD1_3 [Serializable] +#endif public class NUnitEngineException : Exception { /// @@ -46,9 +50,11 @@ public NUnitEngineException(string message) : base(message) { } /// public NUnitEngineException(string message, Exception innerException) : base(message, innerException) { } +#if !NETSTANDARD1_3 /// /// Serialization constructor /// public NUnitEngineException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } diff --git a/src/NUnitEngine/nunit.engine.api/TestFilter.cs b/src/NUnitEngine/nunit.engine.api/TestFilter.cs index e1f197008..44a9c0598 100644 --- a/src/NUnitEngine/nunit.engine.api/TestFilter.cs +++ b/src/NUnitEngine/nunit.engine.api/TestFilter.cs @@ -32,7 +32,7 @@ namespace NUnit.Engine /// In the console runner, filters serve only to carry this /// XML representation, as all filtering is done by the engine. /// -#if !PORTABLE +#if !NETSTANDARD1_3 [Serializable] #endif public class TestFilter diff --git a/src/NUnitEngine/nunit.engine.api/TestPackage.cs b/src/NUnitEngine/nunit.engine.api/TestPackage.cs index 02abe0f59..cdd308541 100644 --- a/src/NUnitEngine/nunit.engine.api/TestPackage.cs +++ b/src/NUnitEngine/nunit.engine.api/TestPackage.cs @@ -33,7 +33,9 @@ namespace NUnit.Engine /// tests for one or more test files. TestPackages may be named /// or anonymous, depending on how they are constructed. /// +#if !NETSTANDARD1_3 [Serializable] +#endif public class TestPackage { #region Constructors @@ -49,7 +51,14 @@ public TestPackage(string filePath) if (filePath != null) { +#if NETSTANDARD1_3 + if (!Path.IsPathRooted(filePath)) + throw new NUnitEngineException("Paths to test assemblies must not be relative in .NET Standard"); + + FullName = filePath; +#else FullName = Path.GetFullPath(filePath); +#endif Settings = new Dictionary(); SubPackages = new List(); } diff --git a/src/NUnitEngine/nunit.engine.api/TestSelectionParserException.cs b/src/NUnitEngine/nunit.engine.api/TestSelectionParserException.cs index 2beb402ef..dfc2cbbc8 100644 --- a/src/NUnitEngine/nunit.engine.api/TestSelectionParserException.cs +++ b/src/NUnitEngine/nunit.engine.api/TestSelectionParserException.cs @@ -22,7 +22,9 @@ // *********************************************************************** using System; +#if !NETSTANDARD1_3 using System.Runtime.Serialization; +#endif namespace NUnit.Engine { @@ -30,7 +32,9 @@ namespace NUnit.Engine /// TestSelectionParserException is thrown when an error /// is found while parsing the selection expression. /// +#if !NETSTANDARD1_3 [Serializable] +#endif public class TestSelectionParserException : Exception { /// @@ -44,10 +48,12 @@ public TestSelectionParserException(string message) : base(message) { } /// /// public TestSelectionParserException(string message, Exception innerException) : base(message, innerException) { } - + +#if !NETSTANDARD1_3 /// /// Serialization constructor /// public TestSelectionParserException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } diff --git a/src/NUnitEngine/nunit.engine.netstandard/Drivers/NUnitNetStandardDriver.cs b/src/NUnitEngine/nunit.engine.netstandard/Drivers/NUnitNetStandardDriver.cs new file mode 100644 index 000000000..d1539f85f --- /dev/null +++ b/src/NUnitEngine/nunit.engine.netstandard/Drivers/NUnitNetStandardDriver.cs @@ -0,0 +1,203 @@ +// *********************************************************************** +// Copyright (c) 2016 Charlie Poole +// +// 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 METHOD +// 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.Linq; +using System.Collections.Generic; +using NUnit.Engine.Internal; +using System.Reflection; +using NUnit.Engine.Extensibility; +using Mono.Cecil; + +namespace NUnit.Engine.Drivers +{ + /// + /// NUnitNetStandardDriver is used by the test-runner to load and run + /// tests using the NUnit framework assembly. + /// + public class NUnitNetStandardDriver : IFrameworkDriver + { + const string LOAD_MESSAGE = "Method called without calling Load first"; + const string INVALID_FRAMEWORK_MESSAGE = "Running tests against this version of the framework using this driver is not supported. Please update NUnit.Framework to the latest version."; + const string FAILED_TO_LOAD_TEST_ASSEMBLY = "Failed to load the test assembly {0}"; + const string FAILED_TO_LOAD_NUNIT = "Failed to load the NUnit Framework in the test assembly"; + + static readonly string CONTROLLER_TYPE = "NUnit.Framework.Api.FrameworkController"; + static readonly string LOAD_METHOD = "LoadTests"; + static readonly string EXPLORE_METHOD = "ExploreTests"; + static readonly string COUNT_METHOD = "CountTests"; + static readonly string RUN_METHOD = "RunTests"; + static readonly string RUN_ASYNC_METHOD = "RunTests"; + static readonly string STOP_RUN_METHOD = "StopRun"; + + static ILogger log = InternalTrace.GetLogger(nameof(NUnitNetStandardDriver)); + + Assembly _testAssembly; + Assembly _frameworkAssembly; + object _frameworkController; + Type _frameworkControllerType; + + /// + /// An id prefix that will be passed to the test framework and used as part of the + /// test ids created. + /// + public string ID { get; set; } + + /// + /// Loads the tests in an assembly. + /// + /// The NUnit Framework that the tests reference + /// The test assembly + /// The test settings + /// An Xml string representing the loaded test + public string Load(string testAssembly, IDictionary settings) + { + var idPrefix = string.IsNullOrEmpty(ID) ? "" : ID + "-"; + + var assemblyRef = AssemblyDefinition.ReadAssembly(testAssembly); + _testAssembly = Assembly.Load(new AssemblyName(assemblyRef.FullName)); + if(_testAssembly == null) + throw new NUnitEngineException(string.Format(FAILED_TO_LOAD_TEST_ASSEMBLY, assemblyRef.FullName)); + + var nunitRef = assemblyRef.MainModule.AssemblyReferences.Where(reference => reference.Name == "nunit.framework").FirstOrDefault(); + if (nunitRef == null) + throw new NUnitEngineException(FAILED_TO_LOAD_NUNIT); + + var nunit = Assembly.Load(new AssemblyName(nunitRef.FullName)); + if (nunit == null) + throw new NUnitEngineException(FAILED_TO_LOAD_NUNIT); + + _frameworkAssembly = nunit; + + _frameworkController = CreateObject(CONTROLLER_TYPE, _testAssembly, idPrefix, settings); + if (_frameworkController == null) + throw new NUnitEngineException(INVALID_FRAMEWORK_MESSAGE); + + _frameworkControllerType = _frameworkController.GetType(); + + log.Info("Loading {0} - see separate log file", _testAssembly.FullName); + return ExecuteMethod(LOAD_METHOD) as string; + } + + /// + /// Counts the number of test cases for the loaded test assembly + /// + /// The XML test filter + /// The number of test cases + public int CountTestCases(string filter) + { + CheckLoadWasCalled(); + object count = ExecuteMethod(COUNT_METHOD, filter); + return count != null ? (int)count : 0; + } + + /// + /// Executes the tests in an assembly. + /// + /// An ITestEventHandler that receives progress notices + /// A filter that controls which tests are executed + /// An Xml string representing the result + public string Run(ITestEventListener listener, string filter) + { + CheckLoadWasCalled(); + log.Info("Running {0} - see separate log file", _testAssembly.FullName); + Action callback = listener != null ? listener.OnTestEvent : (Action)null; + return ExecuteMethod(RUN_METHOD, new[] { typeof(Action), typeof(string) }, callback, filter) as string; + } + + /// + /// Executes the tests in an assembly asyncronously. + /// + /// A callback that receives XML progress notices + /// A filter that controls which tests are executed + public void RunAsync(Action callback, string filter) + { + CheckLoadWasCalled(); + log.Info("Running {0} - see separate log file", _testAssembly.FullName); + ExecuteMethod(RUN_ASYNC_METHOD, new[] { typeof(Action), typeof(string) }, callback, filter); + } + + /// + /// Cancel the ongoing test run. If no test is running, the call is ignored. + /// + /// If true, cancel any ongoing test threads, otherwise wait for them to complete. + public void StopRun(bool force) + { + ExecuteMethod(STOP_RUN_METHOD, force); + } + + /// + /// Returns information about the tests in an assembly. + /// + /// A filter indicating which tests to include + /// An Xml string representing the tests + public string Explore(string filter) + { + CheckLoadWasCalled(); + + log.Info("Exploring {0} - see separate log file", _testAssembly.FullName); + return ExecuteMethod(EXPLORE_METHOD, filter) as string; + } + + #region Helper Methods + + void CheckLoadWasCalled() + { + if (_frameworkController == null) + throw new InvalidOperationException(LOAD_MESSAGE); + } + + object CreateObject(string typeName, params object[] args) + { + var typeinfo = _frameworkAssembly.DefinedTypes.FirstOrDefault(t => t.FullName == typeName); + if (typeinfo == null) + { + log.Error("Could not find type {0}", typeName); + } + return Activator.CreateInstance(typeinfo.AsType(), args); + } + + object ExecuteMethod(string methodName, params object[] args) + { + var method = _frameworkControllerType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance); + return ExecuteMethod(method, args); + } + + object ExecuteMethod(string methodName, Type[] ptypes, params object[] args) + { + var method = _frameworkControllerType.GetMethod(methodName, ptypes); + return ExecuteMethod(method, args); + } + + object ExecuteMethod(MethodInfo method, params object[] args) + { + if (method == null) + { + throw new NUnitEngineException(INVALID_FRAMEWORK_MESSAGE); + } + return method.Invoke(_frameworkController, args); + } + + #endregion + } +} diff --git a/src/NUnitEngine/nunit.engine.netstandard/Drivers/NUnitNetStandardDriverFactory.cs b/src/NUnitEngine/nunit.engine.netstandard/Drivers/NUnitNetStandardDriverFactory.cs new file mode 100644 index 000000000..5cb519820 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.netstandard/Drivers/NUnitNetStandardDriverFactory.cs @@ -0,0 +1,58 @@ +// *********************************************************************** +// Copyright (c) 2014 Charlie Poole +// +// 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.Reflection; +using NUnit.Engine.Extensibility; + +namespace NUnit.Engine.Drivers +{ + public class NUnitNetStandardDriverFactory + { + private const string NUNIT_FRAMEWORK = "nunit.framework"; + + /// + /// Gets a flag indicating whether a given assembly name and version + /// represent a test framework supported by this factory. + /// + /// An AssemblyName referring to the possible test framework. + public bool IsSupportedTestFramework(AssemblyName reference) + { + return reference.Name == NUNIT_FRAMEWORK && reference.Version.Major == 3; + } + + /// + /// Gets a driver for a given test assembly and a framework + /// which the assembly is already known to reference. + /// + /// The domain in which the assembly will be loaded + /// An AssemblyName referring to the test framework. + /// + public IFrameworkDriver GetDriver(AssemblyName reference) + { + Guard.ArgumentValid(IsSupportedTestFramework(reference), "Invalid framework", "reference"); + + return new NUnitNetStandardDriver(); + } + } +} diff --git a/src/NUnitEngine/nunit.engine.netstandard/Runners/DirectTestRunner.cs b/src/NUnitEngine/nunit.engine.netstandard/Runners/DirectTestRunner.cs new file mode 100644 index 000000000..5e18f6d7d --- /dev/null +++ b/src/NUnitEngine/nunit.engine.netstandard/Runners/DirectTestRunner.cs @@ -0,0 +1,162 @@ +// *********************************************************************** +// Copyright (c) 2011-2014 Charlie Poole +// +// 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.Collections.Generic; +using NUnit.Engine.Extensibility; +using NUnit.Engine.Internal; +using NUnit.Engine.Drivers; +using NUnit.Engine.Services; + +namespace NUnit.Engine.Runners +{ + /// + /// DirectTestRunner is the abstract base for runners + /// that deal directly with a framework driver. + /// + public class DirectTestRunner : AbstractTestRunner + { + private readonly List _drivers = new List(); + + public DirectTestRunner(TestPackage package) : base(package) + { + } + + #region AbstractTestRunner Overrides + + /// + /// Explores a previously loaded TestPackage and returns information + /// about the tests found. + /// + /// The TestFilter to be used to select tests + /// + /// A TestEngineResult. + /// + public override TestEngineResult Explore(TestFilter filter) + { + EnsurePackageIsLoaded(); + + var result = new TestEngineResult(); + + foreach (var driver in _drivers) + result.Add(driver.Explore(filter.Text)); + + return result; + } + + /// + /// Load a TestPackage for exploration or execution + /// + /// A TestEngineResult. + protected override TestEngineResult LoadPackage() + { + var result = new TestEngineResult(); + + // DirectRunner may be called with a single-assembly package + // or a set of assemblies as subpackages. + var packages = TestPackage.SubPackages; + if (packages.Count == 0) + packages.Add(TestPackage); + + var driverService = new DriverService(); + + foreach (var subPackage in packages) + { + var testFile = subPackage.FullName; + + bool skipNonTestAssemblies = subPackage.GetSetting(EnginePackageSettings.SkipNonTestAssemblies, false); + + IFrameworkDriver driver = driverService.GetDriver(testFile, skipNonTestAssemblies); + driver.ID = TestPackage.ID; + + result.Add(driver.Load(testFile, subPackage.Settings)); + _drivers.Add(driver); + } + + return result; + } + + /// + /// Count the test cases that would be run under + /// the specified filter. + /// + /// A TestFilter + /// The count of test cases + public override int CountTestCases(TestFilter filter) + { + EnsurePackageIsLoaded(); + + int count = 0; + + foreach (var driver in _drivers) + count += driver.CountTestCases(filter.Text); + + return count; + } + + + /// + /// Run the tests in the loaded TestPackage. + /// + /// An ITestEventHandler to receive events + /// A TestFilter used to select tests + /// + /// A TestEngineResult giving the result of the test execution + /// + protected override TestEngineResult RunTests(ITestEventListener listener, TestFilter filter) + { + EnsurePackageIsLoaded(); + + var result = new TestEngineResult(); + + foreach (var driver in _drivers) + { + result.Add(driver.Run(listener, filter.Text)); + } + + return result; + } + + /// + /// Cancel the ongoing test run. If no test is running, the call is ignored. + /// + /// If true, cancel any ongoing test threads, otherwise wait for them to complete. + public override void StopRun(bool force) + { + foreach(var driver in _drivers) + driver.StopRun(force); + } + + #endregion + + #region Helper Methods + + private void EnsurePackageIsLoaded() + { + if (!IsPackageLoaded) + LoadResult = LoadPackage(); + } + + #endregion + } +} diff --git a/src/NUnitEngine/nunit.engine.netstandard/Runners/MasterTestRunner.cs b/src/NUnitEngine/nunit.engine.netstandard/Runners/MasterTestRunner.cs new file mode 100644 index 000000000..4316dac92 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.netstandard/Runners/MasterTestRunner.cs @@ -0,0 +1,309 @@ +// *********************************************************************** +// Copyright (c) 2011-2014 Charlie Poole +// +// 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.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Xml; +using System.ComponentModel; +using NUnit.Engine.Services; +using NUnit.Engine.Internal; + +namespace NUnit.Engine.Runners +{ + public class MasterTestRunner : ITestRunner + { + private const string TEST_RUN_ELEMENT = "test-run"; + private readonly ITestEngineRunner _engineRunner; + private bool _disposed; + + public MasterTestRunner(TestPackage package) + { + if (package == null) throw new ArgumentNullException("package"); + + TestPackage = package; + + // Get references to the services we use + var runnerFactory = new DefaultTestRunnerFactory(); + _engineRunner = runnerFactory.MakeTestRunner(package); + + InitializePackage(); + } + + #region Properties + + /// + /// The TestPackage for which this is the runner + /// + protected TestPackage TestPackage { get; set; } + + /// + /// The result of the last call to LoadPackage + /// + protected TestEngineResult LoadResult { get; set; } + + /// + /// Gets an indicator of whether the package has been loaded. + /// + protected bool IsPackageLoaded + { + get { return LoadResult != null; } + } + + #endregion + + #region ITestRunner Members + + /// + /// Get a flag indicating whether a test is running + /// + public bool IsTestRunning { get; private set; } + + /// + /// Load a TestPackage for possible execution. The + /// explicit implementation returns an ITestEngineResult + /// for consumption by clients. + /// + /// An XmlNode representing the loaded assembly. + public XmlNode Load() + { + LoadResult = _engineRunner.Load(); + return LoadResult.Xml; + } + + /// + /// Unload any loaded TestPackage. If none is loaded, + /// the call is ignored. + /// + public void Unload() + { + UnloadPackage(); + } + + /// + /// Reload the currently loaded test package. + /// + /// An XmlNode representing the loaded package + /// If no package has been loaded + public XmlNode Reload() + { + LoadResult = _engineRunner.Reload(); + return LoadResult.Xml; + } + + /// + /// Count the test cases that would be run under the specified + /// filter, loading the TestPackage if it is not already loaded. + /// + /// A TestFilter + /// The count of test cases. + public int CountTestCases(TestFilter filter) + { + return _engineRunner.CountTestCases(filter); + } + + /// + /// Run the tests in a loaded TestPackage. The explicit + /// implementation returns an ITestEngineResult for use + /// by external clients. + /// + /// An ITestEventHandler to receive events + /// A TestFilter used to select tests + /// An XmlNode giving the result of the test execution + public XmlNode Run(ITestEventListener listener, TestFilter filter) + { + return RunTests(listener, filter).Xml; + } + + /// + /// Cancel the ongoing test run. If no test is running, the call is ignored. + /// + /// If true, cancel any ongoing test threads, otherwise wait for them to complete. + public void StopRun(bool force) + { + _engineRunner.StopRun(force); + } + + /// + /// Explore a loaded TestPackage and return information about + /// the tests found. + /// + /// A TestFilter used to select tests + /// An XmlNode representing the tests found. + public XmlNode Explore(TestFilter filter) + { + LoadResult = _engineRunner.Explore(filter) + .Aggregate(TEST_RUN_ELEMENT, TestPackage.Name, TestPackage.FullName); + + return LoadResult.Xml; + } + + #endregion + + #region IDisposable + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Dispose of this object. + /// + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing && _engineRunner != null) + _engineRunner.Dispose(); + + _disposed = true; + } + } + + #endregion + + #region Helper Methods + + /// + /// Check the package settings, expand projects and + /// determine what runtimes may be needed. + /// + private void InitializePackage() + { + // Last chance to catch invalid settings in package, + // in case the client runner missed them. + ValidatePackageSettings(); + + // Some files in the top level package may be projects. + // Expand them so that they contain subprojects for + // each contained assembly. + EnsurePackagesAreExpanded(TestPackage); + } + + private void ValidatePackageSettings() + { + // TODO: Any package settings to validate? + } + + private void EnsurePackagesAreExpanded(TestPackage package) + { + if (package == null) throw new ArgumentNullException("package"); + + foreach (var subPackage in package.SubPackages) + { + EnsurePackagesAreExpanded(subPackage); + } + } + + /// + /// Unload any loaded TestPackage. + /// + private void UnloadPackage() + { + LoadResult = null; + if (_engineRunner != null) + _engineRunner.Unload(); + } + + /// + /// Count the test cases that would be run under + /// the specified filter. Returns zero if the + /// package has not yet been loaded. + /// + /// A TestFilter + /// The count of test cases + private int CountTests(TestFilter filter) + { + if (!IsPackageLoaded) return 0; + + return _engineRunner.CountTestCases(filter); + } + + /// + /// Run the tests in the loaded TestPackage and return a test result. The tests + /// are run synchronously and the listener interface is notified as it progresses. + /// + /// An ITestEventHandler to receive events + /// A TestFilter used to select tests + /// A TestEngineResult giving the result of the test execution + private TestEngineResult RunTests(ITestEventListener listener, TestFilter filter) + { + var eventDispatcher = new TestEventDispatcher(); + if (listener != null) + eventDispatcher.Listeners.Add(listener); + + IsTestRunning = true; + + eventDispatcher.OnTestEvent(string.Format("", CountTests(filter))); + + DateTime startTime = DateTime.UtcNow; + long startTicks = Stopwatch.GetTimestamp(); + + TestEngineResult result = _engineRunner.Run(eventDispatcher, filter).Aggregate("test-run", TestPackage.Name, TestPackage.FullName); + + // These are inserted in reverse order, since each is added as the first child. + InsertFilterElement(result.Xml, filter); + + result.Xml.AddAttribute("engine-version", typeof(MasterTestRunner).GetTypeInfo().Assembly.GetName().Version.ToString()); + result.Xml.AddAttribute("clr-version", Microsoft.DotNet.InternalAbstractions.RuntimeEnvironment.GetRuntimeIdentifier()); + + double duration = (double)(Stopwatch.GetTimestamp() - startTicks) / Stopwatch.Frequency; + result.Xml.AddAttribute("start-time", XmlConvert.ToString(startTime, "u")); + result.Xml.AddAttribute("end-time", XmlConvert.ToString(DateTime.UtcNow, "u")); + result.Xml.AddAttribute("duration", duration.ToString("0.000000", NumberFormatInfo.InvariantInfo)); + + IsTestRunning = false; + + eventDispatcher.OnTestEvent(result.Xml.OuterXml); + + return result; + } + + private static void InsertFilterElement(XmlNode resultNode, TestFilter filter) + { + // Convert the filter to an XmlNode + var tempNode = XmlHelper.CreateXmlNode(filter.Text); + + // Don't include it if it's an empty filter + if (tempNode.ChildNodes.Count <= 0) + { + return; + } + + var doc = resultNode.OwnerDocument; + if (doc == null) + { + return; + } + + var filterElement = doc.ImportNode(tempNode, true); + resultNode.InsertAfter(filterElement, null); + } + + #endregion + } +} diff --git a/src/NUnitEngine/nunit.engine.netstandard/Services/DefaultTestRunnerFactory.cs b/src/NUnitEngine/nunit.engine.netstandard/Services/DefaultTestRunnerFactory.cs new file mode 100644 index 000000000..283bcba74 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.netstandard/Services/DefaultTestRunnerFactory.cs @@ -0,0 +1,72 @@ +// *********************************************************************** +// Copyright (c) 2017 Charlie Poole +// +// 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 NUnit.Engine.Internal; +using NUnit.Engine.Runners; + +namespace NUnit.Engine.Services +{ + /// + /// DefaultTestRunnerFactory handles creation of a suitable test + /// runner for a given package to be loaded and run either in a + /// separate process or within the same process. + /// + public class DefaultTestRunnerFactory : ITestRunnerFactory + { + #region InProcessTestRunnerFactory Overrides + + /// + /// Returns a test runner based on the settings in a TestPackage. + /// Any setting that is "consumed" by the factory is removed, so + /// that downstream runners using the factory will not repeatedly + /// create the same type of runner. + /// + /// The TestPackage to be loaded and run + /// A TestRunner + public virtual ITestEngineRunner MakeTestRunner(TestPackage package) + { + int assemblyCount = 0; + + foreach (var subPackage in package.SubPackages) + { + var testFile = subPackage.FullName; + + if (PathUtils.IsAssemblyFileType(testFile)) + assemblyCount++; + } + + if ( assemblyCount > 0) + return new AggregatingTestRunner(package); + + // TODO: What about bad extensions? + return new DirectTestRunner(package); + } + + public virtual bool CanReuse(ITestEngineRunner runner, TestPackage package) + { + return false; + } + + #endregion + } +} diff --git a/src/NUnitEngine/nunit.engine.netstandard/Services/DriverService.cs b/src/NUnitEngine/nunit.engine.netstandard/Services/DriverService.cs new file mode 100644 index 000000000..f403d9d1b --- /dev/null +++ b/src/NUnitEngine/nunit.engine.netstandard/Services/DriverService.cs @@ -0,0 +1,93 @@ +// *********************************************************************** +// Copyright (c) 2017 Charlie Poole +// +// 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.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Linq; +using Mono.Cecil; +using NUnit.Engine.Drivers; +using NUnit.Engine.Extensibility; +using NUnit.Engine.Internal; + +namespace NUnit.Engine.Services +{ + /// + /// The DriverService provides drivers able to load and run tests + /// using various frameworks. + /// + public class DriverService + { + /// + /// Get a driver suitable for use with a particular test assembly. + /// + /// The AppDomain to use for the tests + /// The full path to the test assembly + /// The value of any TargetFrameworkAttribute on the assembly, or null + /// True if non-test assemblies should simply be skipped rather than reporting an error + /// + public IFrameworkDriver GetDriver(string assemblyPath, bool skipNonTestAssemblies) + { + if (!File.Exists(assemblyPath)) + return new InvalidAssemblyFrameworkDriver(assemblyPath, "File not found: " + assemblyPath); + + if (!PathUtils.IsAssemblyFileType(assemblyPath)) + return new InvalidAssemblyFrameworkDriver(assemblyPath, "File type is not supported"); + + try + { + var assemblyDef = AssemblyDefinition.ReadAssembly(assemblyPath); + + if (skipNonTestAssemblies) + { + if (assemblyDef.CustomAttributes.Any(attr => attr.AttributeType.FullName == "NUnit.Framework.NonTestAssemblyAttribute")) + return new SkippedAssemblyFrameworkDriver(assemblyPath); + } + + var references = new List(); + foreach (var cecilRef in assemblyDef.MainModule.AssemblyReferences) + references.Add(new AssemblyName(cecilRef.FullName)); + + var factory = new NUnitNetStandardDriverFactory(); + var driver = references.Where(reference => factory.IsSupportedTestFramework(reference)) + .Select(reference => factory.GetDriver(reference)) + .FirstOrDefault(); + if(driver != null) + { + return driver; + } + } + catch (BadImageFormatException ex) + { + return new InvalidAssemblyFrameworkDriver(assemblyPath, ex.Message); + } + + if (skipNonTestAssemblies) + return new SkippedAssemblyFrameworkDriver(assemblyPath); + else + return new InvalidAssemblyFrameworkDriver(assemblyPath, string.Format("No suitable tests found in '{0}'.\n" + + "Either assembly contains no tests or proper test driver has not been found.", assemblyPath)); + } + } +} diff --git a/src/NUnitEngine/nunit.engine.netstandard/TestEngine.cs b/src/NUnitEngine/nunit.engine.netstandard/TestEngine.cs new file mode 100644 index 000000000..793d1e304 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.netstandard/TestEngine.cs @@ -0,0 +1,108 @@ +// *********************************************************************** +// Copyright (c) 2011 Charlie Poole +// +// 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 NUnit.Engine.Internal; +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; + +namespace NUnit.Engine +{ + /// + /// The TestEngine provides services that allow a client + /// program to interact with NUnit in order to explore, + /// load and run tests. + /// + public class TestEngine : ITestEngine + { + public TestEngine() + { + WorkDirectory = Environment.GetEnvironmentVariable(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "LocalAppData" : "Home"); + InternalTraceLevel = InternalTraceLevel.Default; + } + + #region Public Properties + + public string WorkDirectory { get; set; } + + public InternalTraceLevel InternalTraceLevel { get; set; } + + #endregion + + #region ITestEngine Members + + /// + /// Initialize the engine. This includes initializing mono addins, + /// setting the trace level and creating the standard set of services + /// used in the Engine. + /// + /// This interface is not normally called by user code. Programs linking + /// only to the nunit.engine.api assembly are given a + /// pre-initialized instance of TestEngine. Programs + /// that link directly to nunit.engine usually do so + /// in order to perform custom initialization. + /// + public void Initialize() + { + if (InternalTraceLevel != InternalTraceLevel.Off && !InternalTrace.Initialized) + { + var logName = string.Format("InternalTrace.{0}.log", Process.GetCurrentProcess().Id); + InternalTrace.Initialize(Path.Combine(WorkDirectory, logName), InternalTraceLevel); + } + } + + /// + /// Returns a test runner for use by clients that need to load the + /// tests once and run them multiple times. If necessary, the + /// services are initialized first. + /// + /// An ITestRunner. + public ITestRunner GetRunner(TestPackage package) + { + return new Runners.MasterTestRunner(package); + } + + #endregion + + #region IDisposable Members + + private bool _disposed = false; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + _disposed = true; + } + } + + #endregion + } +} diff --git a/src/NUnitEngine/nunit.engine.netstandard/nunit.engine.netstandard.csproj b/src/NUnitEngine/nunit.engine.netstandard/nunit.engine.netstandard.csproj new file mode 100644 index 000000000..6bd5f0bed --- /dev/null +++ b/src/NUnitEngine/nunit.engine.netstandard/nunit.engine.netstandard.csproj @@ -0,0 +1,165 @@ + + + netstandard1.3 + NUnit.Engine + nunit.engine.netstandard + 3.7.0-dev + + + true + + + ..\..\nunit.snk + True + false + $(PackageVersion) + http://nunit.org/nuget/nunit3-license.txt + http://nunit.org + https://cdn.rawgit.com/nunit/resources/master/images/icon/nunit_256.png + https://github.com/nunit/nunit-console.git + git + nunit test testing tdd engine + + + + + + Extensibility\IFrameworkDriver.cs + + + + ILogger.cs + + + + ISettings.cs + + + + Drivers\NotRunnableFrameworkDriver.cs + + + EnginePackageSettings.cs + + + Guard.cs + + + InternalEnginePackageSettings.cs + + + Internal\CecilExtensions.cs + + + Internal\DirectoryFinder.cs + + + Internal\Logging\InternalTrace.cs + + + Internal\Logging\InternalTraceWriter.cs + + + Internal\Logging\Logger.cs + + + Internal\PathUtils.cs + + + Internal\ResultHelper.cs + + + Internal\SettingsGroup.cs + + + Internal\SettingsStore.cs + + + ITestEngineRunner.cs + + + ITestRunnerFactory.cs + + + + Runners\AbstractTestRunner.cs + + + Runners\AggregatingTestRunner.cs + + + Runners\ITestExecutionTask.cs + + + Runners\TestEventDispatcher.cs + + + Runners\TestExecutionTask.cs + + + + + + + + + + TestEngineResult.cs + + + XmlHelper.cs + + + Extensibility\ExtensionAttribute.cs + + + Extensibility\TypeExtensionPointAttribute.cs + + + InternalTraceLevel.cs + + + ITestEngine.cs + + + ITestEventListener.cs + + + ITestFilterBuilder.cs + + + ITestFilterService.cs + + + ITestRunner.cs + + + NUnitEngineException.cs + + + TestFilter.cs + + + TestPackage.cs + + + + + nunit.snk + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/NUnitEngine/nunit.engine.tests.netstandard/API/TestPackageTests.cs b/src/NUnitEngine/nunit.engine.tests.netstandard/API/TestPackageTests.cs new file mode 100644 index 000000000..037bab2a2 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.tests.netstandard/API/TestPackageTests.cs @@ -0,0 +1,95 @@ +// *********************************************************************** +// Copyright (c) 2017 Charlie Poole +// +// 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 NUnit.Framework; + +namespace NUnit.Engine.Api.Tests +{ + public class TestPackageTests_SingleAssembly + { + private TestPackage package; + + [SetUp] + public void CreatePackage() + { + package = new TestPackage(@"c:\test.dll"); + } + + [Test] + public void PackageIDsAreUnique() + { + var another = new TestPackage(@"c:\another.dll"); + Assert.That(another.ID, Is.Not.EqualTo(package.ID)); + } + + [Test] + public void AssemblyPathIsUsedAsFullName() + { + Assert.AreEqual(@"c:\test.dll", package.FullName); + } + + [Test] + public void FileNameIsUsedAsPackageName() + { + Assert.That(package.Name, Is.EqualTo("test.dll")); + } + + [Test] + public void HasNoSubPackages() + { + Assert.That(package.SubPackages.Count, Is.EqualTo(0)); + } + + [Test] + public void NonRootedPathThrows() + { + Assert.That(() => new TestPackage("test1.dll"), Throws.InstanceOf()); + } + } + + public class TestPackageTests_MultipleAssemblies + { + private TestPackage package; + + [SetUp] + public void CreatePackage() + { + package = new TestPackage(new string[] { @"c:\test1.dll", @"c:\test2.dll", @"c:\test3.dll" }); + } + + [Test] + public void PackageIsAnonymous() + { + Assert.Null(package.FullName); + } + + [Test] + public void PackageContainsThreeSubpackages() + { + Assert.That(package.SubPackages.Count, Is.EqualTo(3)); + Assert.That(package.SubPackages[0].FullName, Is.EqualTo(@"c:\test1.dll")); + Assert.That(package.SubPackages[1].FullName, Is.EqualTo(@"c:\test2.dll")); + Assert.That(package.SubPackages[2].FullName, Is.EqualTo(@"c:\test3.dll")); + } + } +} diff --git a/src/NUnitEngine/nunit.engine.tests.netstandard/Drivers/NUnitNetStandardDriverTests.cs b/src/NUnitEngine/nunit.engine.tests.netstandard/Drivers/NUnitNetStandardDriverTests.cs new file mode 100644 index 000000000..f70bcb7e4 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.tests.netstandard/Drivers/NUnitNetStandardDriverTests.cs @@ -0,0 +1,187 @@ +// *********************************************************************** +// Copyright (c) 2014 Charlie Poole +// +// 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.Collections.Generic; +using System.Web.UI; +using System.Xml; +using NUnit.Tests.Assemblies; +using NUnit.Engine.Internal; +using NUnit.Framework; +using NUnit.Framework.Internal; +using NUnit.Engine.Services; +using NUnit.Engine.Extensibility; + +namespace NUnit.Engine.Drivers.Tests +{ + // Functional tests of the NUnitFrameworkDriver calling into the framework. + public class NUnitNetStandardDriverTests + { + private const string MOCK_ASSEMBLY = "mock-assembly.dll"; + private const string MISSING_FILE = "junk.dll"; + private const string NUNIT_FRAMEWORK = "nunit.framework"; + private const string LOAD_MESSAGE = "Method called without calling Load first"; + + private IDictionary _settings = new Dictionary(); + + private IFrameworkDriver _driver; + private string _mockAssemblyPath; + + [SetUp] + public void CreateDriver() + { + _mockAssemblyPath = System.IO.Path.Combine(TestContext.CurrentContext.TestDirectory, MOCK_ASSEMBLY); + var service = new DriverService(); + _driver = service.GetDriver(_mockAssemblyPath, true); + } + + public void GetsCorrectDriver() + { + Assert.That(_driver, Is.Not.Null); + Assert.That(_driver.GetType(), Is.EqualTo(typeof(NUnitNetStandardDriver))); + } + + #region Load + [Test] + public void Load_GoodFile_ReturnsRunnableSuite() + { + var result = XmlHelper.CreateXmlNode(_driver.Load(_mockAssemblyPath, _settings)); + + Assert.That(result.Name, Is.EqualTo("test-suite")); + Assert.That(result.GetAttribute("type"), Is.EqualTo("Assembly")); + Assert.That(result.GetAttribute("runstate"), Is.EqualTo("Runnable")); + Assert.That(result.GetAttribute("testcasecount"), Is.EqualTo(MockAssembly.Tests.ToString())); + Assert.That(result.SelectNodes("test-suite").Count, Is.EqualTo(0), "Load result should not have child tests"); + } + #endregion + + #region Explore + [Test] + public void Explore_AfterLoad_ReturnsRunnableSuite() + { + _driver.Load(_mockAssemblyPath, _settings); + var result = XmlHelper.CreateXmlNode(_driver.Explore(TestFilter.Empty.Text)); + + Assert.That(result.Name, Is.EqualTo("test-suite")); + Assert.That(result.GetAttribute("type"), Is.EqualTo("Assembly")); + Assert.That(result.GetAttribute("runstate"), Is.EqualTo("Runnable")); + Assert.That(result.GetAttribute("testcasecount"), Is.EqualTo(MockAssembly.Tests.ToString())); + Assert.That(result.SelectNodes("test-suite").Count, Is.GreaterThan(0), "Explore result should have child tests"); + } + + [Test] + public void ExploreTestsAction_WithoutLoad_ThrowsInvalidOperationException() + { + var ex = Assert.Catch(() => _driver.Explore(TestFilter.Empty.Text)); + if (ex is System.Reflection.TargetInvocationException) + ex = ex.InnerException; + Assert.That(ex, Is.TypeOf()); + Assert.That(ex.Message, Is.EqualTo(LOAD_MESSAGE)); + } + #endregion + + #region CountTests + [Test] + public void CountTestsAction_AfterLoad_ReturnsCorrectCount() + { + _driver.Load(_mockAssemblyPath, _settings); + Assert.That(_driver.CountTestCases(TestFilter.Empty.Text), Is.EqualTo(MockAssembly.Tests)); + } + + [Test] + public void CountTestsAction_WithoutLoad_ThrowsInvalidOperationException() + { + var ex = Assert.Catch(() => _driver.CountTestCases(TestFilter.Empty.Text)); + if (ex is System.Reflection.TargetInvocationException) + ex = ex.InnerException; + Assert.That(ex, Is.TypeOf()); + Assert.That(ex.Message, Is.EqualTo(LOAD_MESSAGE)); + } + #endregion + + #region RunTests + [Test] + public void RunTestsAction_AfterLoad_ReturnsRunnableSuite() + { + _driver.Load(_mockAssemblyPath, _settings); + var result = XmlHelper.CreateXmlNode(_driver.Run(new NullListener(), TestFilter.Empty.Text)); + + Assert.That(result.Name, Is.EqualTo("test-suite")); + Assert.That(result.GetAttribute("type"), Is.EqualTo("Assembly")); + Assert.That(result.GetAttribute("runstate"), Is.EqualTo("Runnable")); + Assert.That(result.GetAttribute("testcasecount"), Is.EqualTo(MockAssembly.Tests.ToString())); + Assert.That(result.GetAttribute("result"), Is.EqualTo("Failed")); + Assert.That(result.GetAttribute("passed"), Is.EqualTo(MockAssembly.Passed.ToString())); + Assert.That(result.GetAttribute("failed"), Is.EqualTo(MockAssembly.Failed.ToString())); + Assert.That(result.GetAttribute("skipped"), Is.EqualTo(MockAssembly.Skipped.ToString())); + Assert.That(result.GetAttribute("inconclusive"), Is.EqualTo(MockAssembly.Inconclusive.ToString())); + Assert.That(result.SelectNodes("test-suite").Count, Is.GreaterThan(0), "Explore result should have child tests"); + } + + [Test] + public void RunTestsAction_WithoutLoad_ThrowsInvalidOperationException() + { + var ex = Assert.Catch(() => _driver.Run(new NullListener(), TestFilter.Empty.Text)); + if (ex is System.Reflection.TargetInvocationException) + ex = ex.InnerException; + Assert.That(ex, Is.TypeOf()); + Assert.That(ex.Message, Is.EqualTo(LOAD_MESSAGE)); + } + #endregion + + #region Helper Methods + private static string GetSkipReason(XmlNode result) + { + var propNode = result.SelectSingleNode(string.Format("properties/property[@name='{0}']", PropertyNames.SkipReason)); + return propNode == null ? null : propNode.GetAttribute("value"); + } + #endregion + + #region Nested Callback Class + private class CallbackEventHandler : System.Web.UI.ICallbackEventHandler + { + private string _result; + + public string GetCallbackResult() + { + return _result; + } + + public void RaiseCallbackEvent(string eventArgument) + { + _result = eventArgument; + } + } + #endregion + + #region Nested NullListener Class + public class NullListener : ITestEventListener + { + public void OnTestEvent(string testEvent) + { + // No action + } + } + #endregion + } +} diff --git a/src/NUnitEngine/nunit.engine.tests.netstandard/Program.cs b/src/NUnitEngine/nunit.engine.tests.netstandard/Program.cs new file mode 100644 index 000000000..6d76b00cc --- /dev/null +++ b/src/NUnitEngine/nunit.engine.tests.netstandard/Program.cs @@ -0,0 +1,15 @@ +using NUnitLite; +using System; +using System.Reflection; + +namespace NUnit.Engine.Tests +{ + class Program + { + static int Main(string[] args) + { + int result = new TextRunner(typeof(Program).GetTypeInfo().Assembly).Execute(args); + return result; + } + } +} \ No newline at end of file diff --git a/src/NUnitEngine/nunit.engine.tests.netstandard/Properties/launchSettings.json b/src/NUnitEngine/nunit.engine.tests.netstandard/Properties/launchSettings.json new file mode 100644 index 000000000..fb7f4b28f --- /dev/null +++ b/src/NUnitEngine/nunit.engine.tests.netstandard/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "nunit.engine.tests.netstandard": { + "commandName": "Project", + "commandLineArgs": "--wait" + } + } +} \ No newline at end of file diff --git a/src/NUnitEngine/nunit.engine.tests.netstandard/nunit.engine.tests.netstandard.csproj b/src/NUnitEngine/nunit.engine.tests.netstandard/nunit.engine.tests.netstandard.csproj new file mode 100644 index 000000000..12663a49c --- /dev/null +++ b/src/NUnitEngine/nunit.engine.tests.netstandard/nunit.engine.tests.netstandard.csproj @@ -0,0 +1,49 @@ + + + + Exe + netcoreapp1.1 + True + ..\..\nunit.snk + false + $(PackageVersion) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/NUnitEngine/nunit.engine.tests/Internal/PathUtilTests.cs b/src/NUnitEngine/nunit.engine.tests/Internal/PathUtilTests.cs index bc3bcf770..62f7c2d02 100644 --- a/src/NUnitEngine/nunit.engine.tests/Internal/PathUtilTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Internal/PathUtilTests.cs @@ -8,10 +8,10 @@ // 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 @@ -78,19 +78,20 @@ public void Canonicalize() PathUtils.Canonicalize( @"C:\folder1\.\folder2\..\file.tmp" ) ); Assert.AreEqual( @"folder1\file.tmp", PathUtils.Canonicalize( @"folder1\.\folder2\..\file.tmp" ) ); - Assert.AreEqual( @"folder1\file.tmp", + Assert.AreEqual( @"folder1\file.tmp", PathUtils.Canonicalize( @"folder1\folder2\.\..\file.tmp" ) ); - Assert.AreEqual( @"file.tmp", + Assert.AreEqual( @"file.tmp", PathUtils.Canonicalize( @"folder1\folder2\..\.\..\file.tmp" ) ); - Assert.AreEqual( @"file.tmp", + Assert.AreEqual( @"file.tmp", PathUtils.Canonicalize( @"folder1\folder2\..\..\..\file.tmp" ) ); } - [Test] +#if !NETCOREAPP1_1 + [Test] [Platform(Exclude="Linux,UNIX,MacOSX")] public void RelativePath() { - Assert.AreEqual( @"folder2\folder3", PathUtils.RelativePath( + Assert.AreEqual( @"folder2\folder3", PathUtils.RelativePath( @"c:\folder1", @"c:\folder1\folder2\folder3" ) ); Assert.AreEqual( @"..\folder2\folder3", PathUtils.RelativePath( @"c:\folder1", @"c:\folder2\folder3" ) ); @@ -110,19 +111,20 @@ public void RelativePath() // First filePath consisting just of a root: Assert.AreEqual(@"folder1\folder2", PathUtils.RelativePath( @"C:\", @"C:\folder1\folder2")); - + // Trailing directory separator in first filePath shall be ignored: Assert.AreEqual(@"folder2\folder3", PathUtils.RelativePath( @"c:\folder1\", @"c:\folder1\folder2\folder3")); - + // Case-insensitive behavior, preserving 2nd filePath directories in result: Assert.AreEqual(@"Folder2\Folder3", PathUtils.RelativePath( @"C:\folder1", @"c:\folder1\Folder2\Folder3")); Assert.AreEqual(@"..\Folder2\folder3", PathUtils.RelativePath( @"c:\folder1", @"C:\Folder2\folder3")); } +#endif - [Test] + [Test] public void SamePathOrUnder() { Assert.SamePathOrUnder( @"C:\folder1\folder2\folder3", @"c:\folder1\.\folder2\junk\..\folder3" ); @@ -162,24 +164,24 @@ public void Canonicalize() PathUtils.Canonicalize( "/folder1/./folder2/../file.tmp" ) ); Assert.AreEqual( "folder1/file.tmp", PathUtils.Canonicalize( "folder1/./folder2/../file.tmp" ) ); - Assert.AreEqual( "folder1/file.tmp", + Assert.AreEqual( "folder1/file.tmp", PathUtils.Canonicalize( "folder1/folder2/./../file.tmp" ) ); - Assert.AreEqual( "file.tmp", + Assert.AreEqual( "file.tmp", PathUtils.Canonicalize( "folder1/folder2/.././../file.tmp" ) ); - Assert.AreEqual( "file.tmp", + Assert.AreEqual( "file.tmp", PathUtils.Canonicalize( "folder1/folder2/../../../file.tmp" ) ); } [Test] public void RelativePath() { - Assert.AreEqual( "folder2/folder3", + Assert.AreEqual( "folder2/folder3", PathUtils.RelativePath( "/folder1", "/folder1/folder2/folder3" ) ); - Assert.AreEqual( "../folder2/folder3", + Assert.AreEqual( "../folder2/folder3", PathUtils.RelativePath( "/folder1", "/folder2/folder3" ) ); - Assert.AreEqual( "bin/debug", + Assert.AreEqual( "bin/debug", PathUtils.RelativePath( "/folder1", "bin/debug" ) ); - Assert.AreEqual( "../other/folder", + Assert.AreEqual( "../other/folder", PathUtils.RelativePath( "/folder", "/other/folder" ) ); Assert.AreEqual( "../../d", PathUtils.RelativePath( "/a/b/c", "/a/d" ) ); @@ -187,15 +189,15 @@ public void RelativePath() PathUtils.RelativePath("/a/b", "/a/b")); Assert.AreEqual(string.Empty, PathUtils.RelativePath("/", "/")); - + // First filePath consisting just of a root: Assert.AreEqual("folder1/folder2", PathUtils.RelativePath( "/", "/folder1/folder2")); - + // Trailing directory separator in first filePath shall be ignored: Assert.AreEqual("folder2/folder3", PathUtils.RelativePath( "/folder1/", "/folder1/folder2/folder3")); - + // Case-sensitive behavior: Assert.AreEqual("../Folder1/Folder2/folder3", PathUtils.RelativePath("/folder1", "/Folder1/Folder2/folder3"), diff --git a/src/NUnitEngine/nunit.engine.tests/Internal/SettingsGroupTests.cs b/src/NUnitEngine/nunit.engine.tests/Internal/SettingsGroupTests.cs index b16857401..01a9d753e 100644 --- a/src/NUnitEngine/nunit.engine.tests/Internal/SettingsGroupTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Internal/SettingsGroupTests.cs @@ -23,9 +23,10 @@ using System; using System.ComponentModel; -using System.Drawing; using NUnit.Framework; -using Microsoft.Win32; +#if !NETCOREAPP1_1 +using System.Drawing; +#endif namespace NUnit.Engine.Internal.Tests { @@ -100,6 +101,7 @@ public void WhenSettingIsNotValid_DefaultSettingIsReturned() Assert.AreEqual( 42, settings.GetSetting( "X", 42 ) ); } +#if !NETCOREAPP1_1 [Test] [SetCulture("da-DK")] public void SaveAndGetSettingShouldReturnTheOriginalValue() @@ -113,5 +115,6 @@ public void SaveAndGetSettingShouldReturnTheOriginalValue() var point = settings.GetSetting(settingName, new Point(30, 40)); Assert.That(point, Is.EqualTo(settingValue)); } +#endif } } diff --git a/src/NUnitEngine/nunit.engine.tests/Runners/MasterTestRunnerTests.cs b/src/NUnitEngine/nunit.engine.tests/Runners/MasterTestRunnerTests.cs index 7fa513b26..32181b643 100644 --- a/src/NUnitEngine/nunit.engine.tests/Runners/MasterTestRunnerTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Runners/MasterTestRunnerTests.cs @@ -26,19 +26,26 @@ using System.Xml; using NUnit.Framework; using NUnit.Tests.Assemblies; +using System.Reflection; +using System.IO; namespace NUnit.Engine.Runners.Tests { public class MasterTestRunnerTests : ITestEventListener { private TestPackage _package; +#if !NETCOREAPP1_1 private ServiceContext _services; +#endif private MasterTestRunner _runner; private List _events; [SetUp] public void Initialize() { +#if !NETCOREAPP1_1 + _package = new TestPackage("mock-assembly.dll"); + // Add all services needed _services = new ServiceContext(); _services.Add(new Services.DomainManager()); @@ -50,10 +57,12 @@ public void Initialize() _services.Add(new Services.TestAgency("ProcessRunnerTests", 0)); _services.ServiceManager.StartServices(); - _package = new TestPackage("mock-assembly.dll"); - _runner = new MasterTestRunner(_services, _package); - +#else + var dir = Path.GetDirectoryName(typeof(MasterTestRunnerTests).GetTypeInfo().Assembly.Location); + _package = new TestPackage(Path.Combine(dir, "mock-assembly.dll")); + _runner = new MasterTestRunner(_package); +#endif _events = new List(); } @@ -63,8 +72,10 @@ public void CleanUp() if (_runner != null) _runner.Dispose(); +#if !NETCOREAPP1_1 if (_services != null) _services.ServiceManager.Dispose(); +#endif } [Test] @@ -106,6 +117,7 @@ public void Run() CheckTestRunResult(result); } +#if !NETCOREAPP1_1 [Test] public void RunAsync() { @@ -114,6 +126,7 @@ public void RunAsync() testRun.Wait(-1); CheckTestRunResult(testRun.Result); } +#endif private void CheckTestRunResult(XmlNode result) { diff --git a/src/NUnitEngine/nunit.engine.tests/Services/ResultServiceTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/ResultServiceTests.cs index 2eab918b8..43137e1d2 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/ResultServiceTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/ResultServiceTests.cs @@ -32,6 +32,19 @@ public class ResultServiceTests { private ResultService _resultService; +#if NETCOREAPP1_1 + [SetUp] + public void CreateService() + { + _resultService = new ResultService(); + } + + [Test] + public void AvailableFormats() + { + Assert.That(_resultService.Formats, Is.EquivalentTo(new string[] { "nunit3", "cases" })); + } +#else [SetUp] public void CreateService() { @@ -53,6 +66,7 @@ public void AvailableFormats() { Assert.That(_resultService.Formats, Is.EquivalentTo(new string[] { "nunit3", "cases", "user" })); } +#endif [TestCase("nunit3", null, ExpectedResult = "NUnit3XmlResultWriter")] //[TestCase("nunit2", null, ExpectedResult = "NUnit2XmlResultWriter")] @@ -66,6 +80,7 @@ public string CanGetWriter(string format, object[] args) return writer.GetType().Name; } +#if !NETCOREAPP1_1 [Test] public void CanGetWriterUser() { @@ -88,5 +103,6 @@ public void NUnit3Format_NullArgument_ThrowsArgumentNullException() () => _resultService.GetResultWriter("user", null), Throws.TypeOf()); } +#endif } } diff --git a/src/NUnitEngine/nunit.engine.tests/Services/TestFilteringTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/TestFilteringTests.cs index e832c19d6..e3f0d0fcf 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/TestFilteringTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/TestFilteringTests.cs @@ -35,13 +35,21 @@ public class TestFilteringTests { private const string MOCK_ASSEMBLY = "mock-assembly.dll"; +#if NETCOREAPP1_1 + private NUnitNetStandardDriver _driver; +#else private NUnit3FrameworkDriver _driver; +#endif [SetUp] public void LoadAssembly() { var mockAssemblyPath = System.IO.Path.Combine(TestContext.CurrentContext.TestDirectory, MOCK_ASSEMBLY); +#if NETCOREAPP1_1 + _driver = new NUnitNetStandardDriver(); +#else _driver = new NUnit3FrameworkDriver(AppDomain.CurrentDomain); +#endif _driver.Load(mockAssemblyPath, new Dictionary()); } diff --git a/src/NUnitEngine/nunit.engine/Drivers/NotRunnableFrameworkDriver.cs b/src/NUnitEngine/nunit.engine/Drivers/NotRunnableFrameworkDriver.cs index 5db49fa56..e9e98b98d 100644 --- a/src/NUnitEngine/nunit.engine/Drivers/NotRunnableFrameworkDriver.cs +++ b/src/NUnitEngine/nunit.engine/Drivers/NotRunnableFrameworkDriver.cs @@ -24,6 +24,7 @@ using System.Collections.Generic; using System.IO; using NUnit.Engine.Extensibility; +using System.Reflection; namespace NUnit.Engine.Drivers { @@ -65,6 +66,7 @@ public NotRunnableFrameworkDriver(string assemblyPath, string message) public string ID { get; set; } + public string Load(string assemblyPath, IDictionary settings) { return GetLoadResult(); diff --git a/src/NUnitEngine/nunit.engine/ITestEngineRunner.cs b/src/NUnitEngine/nunit.engine/ITestEngineRunner.cs index a6e52bf68..b68025627 100644 --- a/src/NUnitEngine/nunit.engine/ITestEngineRunner.cs +++ b/src/NUnitEngine/nunit.engine/ITestEngineRunner.cs @@ -68,6 +68,7 @@ public interface ITestEngineRunner : IDisposable /// A TestEngineResult giving the result of the test execution TestEngineResult Run(ITestEventListener listener, TestFilter filter); +#if !NETSTANDARD1_3 /// /// Start a run of the tests in the loaded TestPackage. The tests are run /// asynchronously and the listener interface is notified as it progresses. @@ -76,7 +77,8 @@ public interface ITestEngineRunner : IDisposable /// A TestFilter used to select tests /// An that will provide the result of the test execution AsyncTestEngineResult RunAsync(ITestEventListener listener, TestFilter filter); - +#endif + /// /// Cancel the current test run. If no test is running, /// the call is ignored. diff --git a/src/NUnitEngine/nunit.engine/Internal/DirectoryFinder.cs b/src/NUnitEngine/nunit.engine/Internal/DirectoryFinder.cs index 8ee48aacc..d98daee7b 100644 --- a/src/NUnitEngine/nunit.engine/Internal/DirectoryFinder.cs +++ b/src/NUnitEngine/nunit.engine/Internal/DirectoryFinder.cs @@ -28,8 +28,6 @@ namespace NUnit.Engine.Internal { - #region Public Methods - /// /// DirectoryFinder is a utility class used for extended wildcard /// selection of directories and files. It's less than a full-fledged @@ -37,6 +35,8 @@ namespace NUnit.Engine.Internal /// public static class DirectoryFinder { + #region Public Methods + /// /// Get a list of diretories matching and extended wildcard pattern. /// Each path component may have wildcard characters and a component diff --git a/src/NUnitEngine/nunit.engine/Internal/SettingsStore.cs b/src/NUnitEngine/nunit.engine/Internal/SettingsStore.cs index d72a1fdd9..225f56bf6 100644 --- a/src/NUnitEngine/nunit.engine/Internal/SettingsStore.cs +++ b/src/NUnitEngine/nunit.engine/Internal/SettingsStore.cs @@ -24,10 +24,12 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Drawing; using System.Globalization; using System.IO; using System.Xml; +#if NETSTANDARD1_3 +using System.Xml.Linq; +#endif namespace NUnit.Engine.Internal { @@ -71,18 +73,21 @@ public void LoadSettings() try { XmlDocument doc = new XmlDocument(); - doc.Load(_settingsFile); + using (var stream = new FileStream(_settingsFile, FileMode.Open, FileAccess.Read)) + { + doc.Load(stream); + } foreach (XmlElement element in doc.DocumentElement["Settings"].ChildNodes) { if (element.Name != "Setting") - throw new ApplicationException("Unknown element in settings file: " + element.Name); + throw new Exception("Unknown element in settings file: " + element.Name); if (!element.HasAttribute("name")) - throw new ApplicationException("Setting must have 'name' attribute"); + throw new Exception("Setting must have 'name' attribute"); if (!element.HasAttribute("value")) - throw new ApplicationException("Setting must have 'value' attribute"); + throw new Exception("Setting must have 'value' attribute"); SaveSetting(element.GetAttribute("name"), element.GetAttribute("value")); } @@ -104,6 +109,29 @@ public void SaveSettings() if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath); +#if NETSTANDARD1_3 + var settings = new XElement("Settings"); + + List keys = new List(_settings.Keys); + keys.Sort(); + + foreach (string name in keys) + { + object val = GetSetting(name); + if (val != null) + { + settings.Add(new XElement("Setting", + new XAttribute("name", name), + new XAttribute("value", TypeDescriptor.GetConverter(val.GetType()).ConvertToInvariantString(val)) + )); + } + } + var doc = new XDocument(new XElement("NUnitSettings", settings)); + using (var file = new FileStream(_settingsFile, FileMode.Create, FileAccess.Write)) + { + doc.Save(file); + } +#else XmlTextWriter writer = new XmlTextWriter(_settingsFile, System.Text.Encoding.UTF8); writer.Formatting = Formatting.Indented; @@ -130,6 +158,7 @@ public void SaveSettings() writer.WriteEndElement(); writer.WriteEndElement(); writer.Close(); +#endif } catch (Exception) { diff --git a/src/NUnitEngine/nunit.engine/Properties/AssemblyInfo.cs b/src/NUnitEngine/nunit.engine/Properties/AssemblyInfo.cs index 0c5a50fe2..d36ec2820 100644 --- a/src/NUnitEngine/nunit.engine/Properties/AssemblyInfo.cs +++ b/src/NUnitEngine/nunit.engine/Properties/AssemblyInfo.cs @@ -5,8 +5,13 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. +#if NETSTANDARD1_3 +[assembly: AssemblyTitle("NUnit .NET Standard Engine")] +[assembly: AssemblyDescription("Provides a common interface for loading, exploring and running NUnit tests in .NET Core and .NET Standard")] +#else [assembly: AssemblyTitle("NUnit Engine")] [assembly: AssemblyDescription("Provides a common interface for loading, exploring and running NUnit tests")] +#endif [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible diff --git a/src/NUnitEngine/nunit.engine/Runners/AbstractTestRunner.cs b/src/NUnitEngine/nunit.engine/Runners/AbstractTestRunner.cs index e6fc72b33..b21c42222 100644 --- a/src/NUnitEngine/nunit.engine/Runners/AbstractTestRunner.cs +++ b/src/NUnitEngine/nunit.engine/Runners/AbstractTestRunner.cs @@ -36,6 +36,13 @@ namespace NUnit.Engine.Runners /// public abstract class AbstractTestRunner : ITestEngineRunner { +#if NETSTANDARD1_3 + public AbstractTestRunner(TestPackage package) + { + TestRunnerFactory = new DefaultTestRunnerFactory(); + TestPackage = package; + } +#else public AbstractTestRunner(IServiceLocator services, TestPackage package) { Services = services; @@ -43,15 +50,18 @@ public AbstractTestRunner(IServiceLocator services, TestPackage package) ProjectService = Services.GetService(); TestPackage = package; } +#endif #region Properties +#if !NETSTANDARD1_3 /// /// Our Service Context /// protected IServiceLocator Services { get; private set; } protected IProjectService ProjectService { get; private set; } +#endif protected ITestRunnerFactory TestRunnerFactory { get; private set; } @@ -108,7 +118,8 @@ public virtual void UnloadPackage() /// A TestFilter used to select tests /// A TestEngineResult giving the result of the test execution protected abstract TestEngineResult RunTests(ITestEventListener listener, TestFilter filter); - + +#if !NETSTANDARD1_3 /// /// Start a run of the tests in the loaded TestPackage, returning immediately. /// The tests are run asynchronously and the listener interface is notified @@ -133,6 +144,7 @@ protected virtual AsyncTestEngineResult RunTestsAsync(ITestEventListener listene return testRun; } +#endif /// /// Cancel the ongoing test run. If no test is running, the call is ignored. @@ -203,6 +215,7 @@ public TestEngineResult Run(ITestEventListener listener, TestFilter filter) return RunTests(listener, filter); } +#if !NETSTANDARD1_3 /// /// Start a run of the tests in the loaded TestPackage. The tests are run /// asynchronously and the listener interface is notified as it progresses. @@ -214,6 +227,7 @@ public AsyncTestEngineResult RunAsync(ITestEventListener listener, TestFilter fi { return RunTestsAsync(listener, filter); } +#endif #endregion diff --git a/src/NUnitEngine/nunit.engine/Runners/AggregatingTestRunner.cs b/src/NUnitEngine/nunit.engine/Runners/AggregatingTestRunner.cs index 202856c14..68d9bec44 100644 --- a/src/NUnitEngine/nunit.engine/Runners/AggregatingTestRunner.cs +++ b/src/NUnitEngine/nunit.engine/Runners/AggregatingTestRunner.cs @@ -63,9 +63,15 @@ public IList Runners } } +#if NETSTANDARD1_3 + public AggregatingTestRunner(TestPackage package) : base(package) + { + } +#else public AggregatingTestRunner(IServiceLocator services, TestPackage package) : base(services, package) { } +#endif #region AbstractTestRunner Overrides @@ -142,37 +148,52 @@ protected override TestEngineResult RunTests(ITestEventListener listener, TestFi bool disposeRunners = TestPackage.GetSetting(EnginePackageSettings.DisposeRunners, false); +#if NETSTANDARD1_3 + RunTestsSequentially(listener, filter, results, disposeRunners); +#else if (LevelOfParallelism <= 1) { - foreach (ITestEngineRunner runner in Runners) - { - results.Add(runner.Run(listener, filter)); - if (disposeRunners) runner.Dispose(); - } + RunTestsSequentially(listener, filter, results, disposeRunners); } else { - var workerPool = new ParallelTaskWorkerPool(LevelOfParallelism); - var tasks = new List(); + RunTestsInParallel(listener, filter, results, disposeRunners); + } +#endif + if (disposeRunners) Runners.Clear(); - foreach (ITestEngineRunner runner in Runners) - { - var task = new TestExecutionTask(runner, listener, filter, disposeRunners); - tasks.Add(task); - workerPool.Enqueue(task); - } + return ResultHelper.Merge(results); + } - workerPool.Start(); - workerPool.WaitAll(); + private void RunTestsSequentially(ITestEventListener listener, TestFilter filter, List results, bool disposeRunners) + { + foreach (ITestEngineRunner runner in Runners) + { + results.Add(runner.Run(listener, filter)); + if (disposeRunners) runner.Dispose(); + } + } + +#if !NETSTANDARD1_3 + private void RunTestsInParallel(ITestEventListener listener, TestFilter filter, List results, bool disposeRunners) + { + var workerPool = new ParallelTaskWorkerPool(LevelOfParallelism); + var tasks = new List(); - foreach (var task in tasks) - results.Add(task.Result()); + foreach (ITestEngineRunner runner in Runners) + { + var task = new TestExecutionTask(runner, listener, filter, disposeRunners); + tasks.Add(task); + workerPool.Enqueue(task); } - if (disposeRunners) Runners.Clear(); + workerPool.Start(); + workerPool.WaitAll(); - return ResultHelper.Merge(results); + foreach (var task in tasks) + results.Add(task.Result()); } +#endif /// /// Cancel the ongoing test run. If no test is running, the call is ignored. @@ -194,7 +215,7 @@ protected override void Dispose(bool disposing) Runners.Clear(); } - #endregion +#endregion protected virtual ITestEngineRunner CreateRunner(TestPackage package) { diff --git a/src/NUnitEngine/nunit.engine/Runners/TestEventDispatcher.cs b/src/NUnitEngine/nunit.engine/Runners/TestEventDispatcher.cs index 99484e7b6..2318b41b9 100644 --- a/src/NUnitEngine/nunit.engine/Runners/TestEventDispatcher.cs +++ b/src/NUnitEngine/nunit.engine/Runners/TestEventDispatcher.cs @@ -29,7 +29,11 @@ namespace NUnit.Engine.Runners /// /// TestEventDispatcher is used to send test events to a number of listeners /// - public class TestEventDispatcher : MarshalByRefObject, ITestEventListener + public class TestEventDispatcher : +#if !NETSTANDARD1_3 + MarshalByRefObject, +#endif + ITestEventListener { private object _eventLock = new object(); @@ -49,9 +53,11 @@ public void OnTestEvent(string report) } } +#if !NETSTANDARD1_3 public override object InitializeLifetimeService() { return null; } +#endif } } diff --git a/src/NUnitEngine/nunit.engine/Services/ResultService.cs b/src/NUnitEngine/nunit.engine/Services/ResultService.cs index 518d47cd8..3fd06c11c 100644 --- a/src/NUnitEngine/nunit.engine/Services/ResultService.cs +++ b/src/NUnitEngine/nunit.engine/Services/ResultService.cs @@ -27,11 +27,19 @@ namespace NUnit.Engine.Services { - public class ResultService : Service, IResultService + public class ResultService : +#if !NETSTANDARD1_3 + Service, +#endif + IResultService { +#if NETSTANDARD1_3 + private readonly string[] BUILT_IN_FORMATS = new string[] { "nunit3", "cases" }; +#else private readonly string[] BUILT_IN_FORMATS = new string[] { "nunit3", "cases", "user" }; private IEnumerable _extensionNodes; +#endif private string[] _formats; public string[] Formats @@ -42,9 +50,11 @@ public string[] Formats { var formatList = new List(BUILT_IN_FORMATS); +#if !NETSTANDARD1_3 foreach (var node in _extensionNodes) foreach (var format in node.GetValues("Format")) formatList.Add(format); +#endif _formats = formatList.ToArray(); } @@ -67,6 +77,10 @@ public IResultWriter GetResultWriter(string format, object[] args) return new NUnit3XmlResultWriter(); case "cases": return new TestCaseResultWriter(); +#if NETSTANDARD1_3 + default: + return null; +#else case "user": return new XmlTransformResultWriter(args); default: @@ -76,9 +90,11 @@ public IResultWriter GetResultWriter(string format, object[] args) return node.ExtensionObject as IResultWriter; return null; +#endif } } +#if !NETSTANDARD1_3 #region IService Members public override void StartService() @@ -101,5 +117,6 @@ public override void StartService() } #endregion +#endif } } diff --git a/src/NUnitEngine/nunit.engine/Services/ResultWriters/NUnit3XmlResultWriter.cs b/src/NUnitEngine/nunit.engine/Services/ResultWriters/NUnit3XmlResultWriter.cs index 57c6f513d..6ff0bf74b 100644 --- a/src/NUnitEngine/nunit.engine/Services/ResultWriters/NUnit3XmlResultWriter.cs +++ b/src/NUnitEngine/nunit.engine/Services/ResultWriters/NUnit3XmlResultWriter.cs @@ -50,7 +50,8 @@ public void CheckWritability(string outputPath) public void WriteResultFile(XmlNode resultNode, string outputPath) { - using (var writer = new StreamWriter(outputPath, false, Encoding.UTF8)) + using (var stream = new FileStream(outputPath, FileMode.Create, FileAccess.Write)) + using (var writer = new StreamWriter(stream)) { WriteResultFile(resultNode, writer); } @@ -75,10 +76,12 @@ private XmlNode GetStubResult() test.AddAttribute("start-time", DateTime.UtcNow.ToString("u")); doc.AppendChild(test); +#if !NETSTANDARD1_3 var cmd = doc.CreateElement("command-line"); var cdata = doc.CreateCDataSection(Environment.CommandLine); cmd.AppendChild(cdata); test.AppendChild(cmd); +#endif return doc; } } diff --git a/src/NUnitEngine/nunit.engine/Services/ResultWriters/TestCaseResultWriter.cs b/src/NUnitEngine/nunit.engine/Services/ResultWriters/TestCaseResultWriter.cs index 990fbfa50..b2601c4b5 100644 --- a/src/NUnitEngine/nunit.engine/Services/ResultWriters/TestCaseResultWriter.cs +++ b/src/NUnitEngine/nunit.engine/Services/ResultWriters/TestCaseResultWriter.cs @@ -32,7 +32,7 @@ public class TestCaseResultWriter : IResultWriter { public void CheckWritability(string outputPath) { - using (new StreamWriter(outputPath, false, Encoding.UTF8)) + using (new FileStream(outputPath, FileMode.Create, FileAccess.Write)) { // Opening is enough to check } @@ -40,7 +40,8 @@ public void CheckWritability(string outputPath) public void WriteResultFile(XmlNode resultNode, string outputPath) { - using (var writer = new StreamWriter(outputPath, false, Encoding.UTF8)) + using (var stream = new FileStream(outputPath, FileMode.Create, FileAccess.Write)) + using (var writer = new StreamWriter(stream)) { WriteResultFile(resultNode, writer); } diff --git a/src/NUnitEngine/nunit.engine/Services/TestFilterService.cs b/src/NUnitEngine/nunit.engine/Services/TestFilterService.cs index 1af4257de..c5c9a0215 100644 --- a/src/NUnitEngine/nunit.engine/Services/TestFilterService.cs +++ b/src/NUnitEngine/nunit.engine/Services/TestFilterService.cs @@ -23,7 +23,11 @@ namespace NUnit.Engine.Services { - public class TestFilterService : Service, ITestFilterService + public class TestFilterService : +#if !NETSTANDARD1_3 + Service, +#endif + ITestFilterService { public ITestFilterBuilder GetTestFilterBuilder() { diff --git a/src/NUnitEngine/nunit.engine/TestEngineResult.cs b/src/NUnitEngine/nunit.engine/TestEngineResult.cs index b703d0853..e9a00ae9f 100644 --- a/src/NUnitEngine/nunit.engine/TestEngineResult.cs +++ b/src/NUnitEngine/nunit.engine/TestEngineResult.cs @@ -47,12 +47,16 @@ namespace NUnit.Engine /// for combining multiple TestEngineResults into one. /// /// + #if !NETSTANDARD1_3 [Serializable] +#endif public class TestEngineResult { private List _xmlText = new List(); +#if !NETSTANDARD1_3 [NonSerialized] +#endif private List _xmlNodes = new List(); #region Constructors diff --git a/tools/packages.config b/tools/packages.config index ff1c6fed5..ae56151aa 100644 --- a/tools/packages.config +++ b/tools/packages.config @@ -1,4 +1,4 @@ - +