diff --git a/cake/constants.cake b/cake/constants.cake
index 6d4e35650..5729afd89 100644
--- a/cake/constants.cake
+++ b/cake/constants.cake
@@ -33,10 +33,10 @@ var ENGINE_TESTS_PROJECT = SOURCE_DIR + "NUnitEngine/nunit.engine.tests/nunit.en
var ENGINE_CORE_TESTS_PROJECT = SOURCE_DIR + "NUnitEngine/nunit.engine.core.tests/nunit.engine.core.tests.csproj";
var CONSOLE_PROJECT = SOURCE_DIR + "NUnitConsole/nunit-console/nunit-console.csproj";
var CONSOLE_TESTS_PROJECT = SOURCE_DIR + "NUnitConsole/nunit-console.tests/nunit-console.tests.csproj";
-var MOCK_ASSEMBLY_PROJECT = SOURCE_DIR + "NUnitEngine/mock-assembly/mock-assembly.csproj";
-var MOCK_ASSEMBLY_X86_PROJECT = SOURCE_DIR + "NUnitEngine/mock-assembly-x86/mock-assembly-x86.csproj";
-var MOCK_ASSEMBLY_NUNIT4_PROJECT = SOURCE_DIR + "NUnitEngine/mock-assembly-nunit4/mock-assembly-nunit4.csproj";
-var NOTEST_PROJECT = SOURCE_DIR + "NUnitEngine/notest-assembly/notest-assembly.csproj";
+var MOCK_ASSEMBLY_PROJECT = SOURCE_DIR + "TestData/mock-assembly/mock-assembly.csproj";
+var MOCK_ASSEMBLY_X86_PROJECT = SOURCE_DIR + "TestData/mock-assembly-x86/mock-assembly-x86.csproj";
+var MOCK_ASSEMBLY_NUNIT4_PROJECT = SOURCE_DIR + "TestData/mock-assembly-nunit4/mock-assembly-nunit4.csproj";
+var NOTEST_PROJECT = SOURCE_DIR + "TestData/notest-assembly/notest-assembly.csproj";
// Console Runner
var NET20_CONSOLE = BIN_DIR + "net20/nunit-console.exe";
var NET60_CONSOLE = BIN_DIR + "net6.0/nunit-console.dll";
diff --git a/src/NUnitEngine/nunit-agent/Program.cs b/src/NUnitEngine/nunit-agent/Program.cs
index fa68708d5..953c66482 100644
--- a/src/NUnitEngine/nunit-agent/Program.cs
+++ b/src/NUnitEngine/nunit-agent/Program.cs
@@ -5,15 +5,34 @@
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
+using Microsoft.Win32;
using NUnit.Common;
using NUnit.Engine;
using NUnit.Engine.Agents;
using NUnit.Engine.Internal;
+#if NETFRAMEWORK
+using RuntimeInformation = NUnit.Engine.Internal.Backports.RuntimeInformation;
+#endif
+
namespace NUnit.Agent
{
public class NUnitTestAgent
{
+ static readonly string CURRENT_RUNTIME = RuntimeInformation.FrameworkDescription;
+ const string AGENT_RUNTIME =
+#if NET6_0
+ ".NET 6.0";
+#elif NET5_0
+ ".NET 5.0";
+#elif NETCOREAPP3_1
+ ".NET Core 3.1";
+#elif NET40
+ ".NET 4.0";
+#elif NET20
+ ".NET 2.0";
+#endif
+
static Guid AgentId;
static string AgencyUrl;
static Process AgencyProcess;
@@ -63,21 +82,14 @@ public static void Main(string[] args)
InternalTrace.Initialize(Path.Combine(workDirectory, logName), traceLevel);
log = InternalTrace.GetLogger(typeof(NUnitTestAgent));
- log.Info("Agent process {0} starting", pid);
+ log.Info($"Agent process {pid} starting");
+ log.Info($"Running {AGENT_RUNTIME} agent under {CURRENT_RUNTIME}");
if (debugArgPassed)
TryLaunchDebugger();
LocateAgencyProcess(agencyPid);
-#if NETCOREAPP3_1
- log.Info($"Running .NET Core 3.1 agent under {RuntimeInformation.FrameworkDescription}");
-#elif NET40
- log.Info($"Running .NET 4.0 agent under {RuntimeFramework.CurrentFramework.DisplayName}");
-#elif NET20
- log.Info($"Running .NET 2.0 agent under {RuntimeFramework.CurrentFramework.DisplayName}");
-#endif
-
log.Info("Starting RemoteTestAgent");
Agent = new RemoteTestAgent(AgentId);
Agent.Transport =
diff --git a/src/NUnitEngine/nunit.engine.api/IRuntimeFrameworkService.cs b/src/NUnitEngine/nunit.engine.api/IRuntimeFrameworkService.cs
index 0e48ebe6d..0f9b9cbd9 100644
--- a/src/NUnitEngine/nunit.engine.api/IRuntimeFrameworkService.cs
+++ b/src/NUnitEngine/nunit.engine.api/IRuntimeFrameworkService.cs
@@ -11,6 +11,12 @@ namespace NUnit.Engine
///
public interface IRuntimeFrameworkService
{
+ ///
+ /// Gets a RuntimeFramework instance representing the runtime under
+ /// which the code is currently running.
+ ///
+ IRuntimeFramework CurrentFramework { get; }
+
///
/// Returns true if the runtime framework represented by
/// the string passed as an argument is available.
diff --git a/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionAssemblyTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionAssemblyTests.cs
index 4a5a6121b..e8ca54e31 100644
--- a/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionAssemblyTests.cs
+++ b/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionAssemblyTests.cs
@@ -1,62 +1,54 @@
// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
using System;
-using System.Reflection;
+using System.IO;
using NUnit.Engine.Extensibility;
using NUnit.Framework;
-namespace NUnit.Engine.Tests.Extensibility
+#if NETFRAMEWORK
+using FrameworkName = NUnit.Engine.Compatibility.FrameworkName;
+#else
+using FrameworkName = System.Runtime.Versioning.FrameworkName;
+#endif
+
+namespace NUnit.Engine.Extensibility
{
+ // TODO: This should actually give us 3.5
+ [TestFixture("../net35/mock-assembly.dll", FrameworkIdentifiers.NetFramework, "2.0")]
+ [TestFixture("../netcoreapp2.1/mock-assembly.dll", FrameworkIdentifiers.NetCoreApp, "2.1")]
+ [TestFixture("../netcoreapp3.1/mock-assembly.dll", FrameworkIdentifiers.NetCoreApp, "3.1")]
+ [TestFixture("../net5.0/mock-assembly.dll", FrameworkIdentifiers.NetCoreApp, "5.0")]
+ [TestFixture("../net6.0/mock-assembly.dll", FrameworkIdentifiers.NetCoreApp, "6.0")]
public class ExtensionAssemblyTests
{
- private static readonly Assembly THIS_ASSEMBLY = Assembly.GetExecutingAssembly();
- private static readonly string THIS_ASSEMBLY_PATH = THIS_ASSEMBLY.Location;
- private static readonly string THIS_ASSEMBLY_FULL_NAME = THIS_ASSEMBLY.GetName().FullName;
- private static readonly string THIS_ASSEMBLY_NAME = THIS_ASSEMBLY.GetName().Name;
- private static readonly Version THIS_ASSEMBLY_VERSION = THIS_ASSEMBLY.GetName().Version;
-
+ private string _assemblyPath;
+ private string _assemblyFileName;
+ private FrameworkName _expectedTargetRuntime;
private ExtensionAssembly _ea;
- [OneTimeSetUp]
- public void CreateExtensionAssemblies()
- {
- _ea = new ExtensionAssembly(THIS_ASSEMBLY_PATH, false);
- }
-
- [Test]
- public void AssemblyDefinition()
+ public ExtensionAssemblyTests(string assemblyPath, string expectedRuntime, string expectedVersion)
{
- Assert.That(_ea.Assembly.FullName, Is.EqualTo(THIS_ASSEMBLY_FULL_NAME));
+ _assemblyPath = assemblyPath;
+ _assemblyFileName = Path.GetFileNameWithoutExtension(assemblyPath);
+ _expectedTargetRuntime = new FrameworkName(expectedRuntime, new Version(expectedVersion));
}
- [Test]
- public void MainModule()
+ [OneTimeSetUp]
+ public void CreateExtensionAssemblies()
{
- Assert.That(_ea.MainModule.Assembly.FullName, Is.EqualTo(THIS_ASSEMBLY_FULL_NAME));
+ _ea = new ExtensionAssembly(_assemblyPath, false);
}
[Test]
public void AssemblyName()
{
- Assert.That(_ea.AssemblyName, Is.EqualTo(THIS_ASSEMBLY_NAME));
+ Assert.That(_ea.AssemblyName, Is.EqualTo(_assemblyFileName));
}
- [Test]
- public void AssemblyVersion()
- {
- Assert.That(_ea.AssemblyVersion, Is.EqualTo(THIS_ASSEMBLY_VERSION));
- }
-
-#if NETFRAMEWORK
[Test]
public void TargetFramework()
{
- Assert.Multiple(() =>
- {
- Assert.That(_ea.TargetFramework, Has.Property(nameof(RuntimeFramework.Runtime)).EqualTo(RuntimeType.Any));
- Assert.That(_ea.TargetFramework, Has.Property(nameof(RuntimeFramework.FrameworkVersion)).EqualTo(new Version(2, 0)));
- });
+ Assert.That(_ea.TargetRuntime, Is.EqualTo(_expectedTargetRuntime));
}
-#endif
}
}
diff --git a/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionSelectorTests.cs b/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionSelectorTests.cs
index 2d381c799..8fb4802a9 100644
--- a/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionSelectorTests.cs
+++ b/src/NUnitEngine/nunit.engine.core.tests/Extensibility/ExtensionSelectorTests.cs
@@ -4,6 +4,7 @@
#if NETFRAMEWORK
using System;
using NSubstitute;
+using NUnit.Engine.Compatibility;
using NUnit.Engine.Extensibility;
using NUnit.Framework;
@@ -112,7 +113,7 @@ private static IExtensionAssembly MockExtension(string assemblyName = "Extension
sub.AssemblyName.Returns(assemblyName);
sub.AssemblyVersion.Returns(assemblyVersion ?? new Version(1, 0));
targetFramework = targetFramework ?? new Version(2, 0);
- sub.TargetFramework.Returns(new RuntimeFramework(RuntimeType.Any, targetFramework));
+ sub.TargetRuntime.Returns(new FrameworkName(FrameworkIdentifiers.NetFramework, targetFramework));
sub.FromWildCard.Returns(fromWildcard);
return sub;
}
diff --git a/src/NUnitEngine/nunit.engine.core.tests/RuntimeFrameworkTests.cs b/src/NUnitEngine/nunit.engine.core.tests/RuntimeFrameworkTests.cs
deleted file mode 100644
index cbf6398c2..000000000
--- a/src/NUnitEngine/nunit.engine.core.tests/RuntimeFrameworkTests.cs
+++ /dev/null
@@ -1,285 +0,0 @@
-// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
-
-#if NETFRAMEWORK
-using System;
-using System.Collections.Generic;
-using NUnit.Framework;
-
-namespace NUnit.Engine
-{
- [TestFixture]
- public class RuntimeFrameworkTests
- {
- static RuntimeType currentRuntime =
- Type.GetType("Mono.Runtime", false) != null
- ? RuntimeType.Mono
- : RuntimeType.Net;
-
- [Test]
- public void CanGetCurrentFramework()
- {
- RuntimeFramework framework = RuntimeFramework.CurrentFramework;
-
- Assert.That(framework.Runtime, Is.EqualTo(currentRuntime));
- Assert.That(framework.ClrVersion, Is.EqualTo(Environment.Version));
- }
-
- [Test]
- public void CurrentFrameworkHasBuildSpecified()
- {
- Assert.That(RuntimeFramework.CurrentFramework.ClrVersion.Build, Is.GreaterThan(0));
- }
-
- [Test]
- public void CurrentFrameworkMustBeAvailable()
- {
- var current = RuntimeFramework.CurrentFramework;
- Console.WriteLine("Current framework is {0} ({1})", current.DisplayName, current.Id);
- Assert.That(current.IsAvailable, "{0} not available", current);
- }
-
- [Test]
- public void AvailableFrameworksList()
- {
- RuntimeFramework[] available = RuntimeFramework.AvailableFrameworks;
- Assert.That(RuntimeFramework.AvailableFrameworks.Length, Is.GreaterThan(0) );
- foreach (var framework in RuntimeFramework.AvailableFrameworks)
- Console.WriteLine("Available: {0}", framework.DisplayName);
- }
-
- [Test]
- public void AvailableFrameworksList_IncludesCurrentFramework()
- {
- foreach (var framework in RuntimeFramework.AvailableFrameworks)
- if (RuntimeFramework.CurrentFramework.Supports(framework))
- return;
-
- Assert.Fail("CurrentFramework not listed as available");
- }
-
- [Test]
- public void AvailableFrameworksList_ContainsNoDuplicates()
- {
- var names = new List();
- foreach (var framework in RuntimeFramework.AvailableFrameworks)
- names.Add(framework.DisplayName);
- Assert.That(names, Is.Unique);
- }
-
- [TestCaseSource(nameof(frameworkData))]
- public void CanCreateUsingFrameworkVersion(FrameworkData data)
- {
- RuntimeFramework framework = new RuntimeFramework(data.runtime, data.frameworkVersion);
- Assert.That(framework.Runtime, Is.EqualTo(data.runtime));
- Assert.That(framework.FrameworkVersion, Is.EqualTo(data.frameworkVersion));
- Assert.That(framework.ClrVersion, Is.EqualTo(data.clrVersion));
- }
-
- [TestCaseSource(nameof(frameworkData))]
- public void CanCreateUsingClrVersion(FrameworkData data)
- {
- // Versions 3.x and 4.5 or higher can't be created using CLR version
- Assume.That(data.frameworkVersion.Major != 3);
- Assume.That(data.frameworkVersion.Major != 4 || data.frameworkVersion.Minor == 0);
-
-
- RuntimeFramework framework = new RuntimeFramework(data.runtime, data.clrVersion);
- Assert.That(framework.Runtime, Is.EqualTo(data.runtime));
- Assert.That(framework.FrameworkVersion, Is.EqualTo(data.frameworkVersion));
- Assert.That(framework.ClrVersion, Is.EqualTo(data.clrVersion));
- }
-
- [TestCaseSource(nameof(frameworkData))]
- public void CanParseRuntimeFramework(FrameworkData data)
- {
- RuntimeFramework framework = RuntimeFramework.Parse(data.representation);
- Assert.That(framework.Runtime, Is.EqualTo(data.runtime));
- Assert.That(framework.ClrVersion, Is.EqualTo(data.clrVersion));
- }
-
- [TestCaseSource(nameof(frameworkData))]
- public void CanDisplayFrameworkAsString(FrameworkData data)
- {
- RuntimeFramework framework = new RuntimeFramework(data.runtime, data.frameworkVersion);
- Assert.That(framework.ToString(), Is.EqualTo(data.representation));
- Assert.That(framework.DisplayName, Is.EqualTo(data.displayName));
- }
-
- [TestCaseSource(nameof(matchData))]
- public bool CanMatchRuntimes(RuntimeFramework f1, RuntimeFramework f2)
- {
- return f1.Supports(f2);
- }
-
- [TestCaseSource(nameof(CanLoadData))]
- public bool CanLoad(RuntimeFramework f1, RuntimeFramework f2)
- {
- return f1.CanLoad(f2);
- }
-
-#pragma warning disable 414
- static TestCaseData[] matchData = new TestCaseData[] {
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(3,5)),
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Net, new Version(3,5)))
- .Returns(false),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(3,5)),
- new RuntimeFramework(RuntimeType.Net, new Version(3,5)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Net, new Version(2,0,50727)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(2,0,50727)),
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(2,0,50727)),
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Mono, new Version(2,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Mono, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(4,0)),
- new RuntimeFramework(RuntimeType.Mono, new Version(4,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Mono, new Version(4,0)),
- new RuntimeFramework(RuntimeType.Net, new Version(4,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Net, new Version(1,1)))
- .Returns(false),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(2,0,50727)),
- new RuntimeFramework(RuntimeType.Net, new Version(2,0,40607)))
- .Returns(false),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Mono, new Version(1,1)), // non-existent version but it works
- new RuntimeFramework(RuntimeType.Mono, new Version(1,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Mono, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Any, new Version(2,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Any, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Mono, new Version(2,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Any, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Any, new Version(2,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Any, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Any, new Version(4,0)))
- .Returns(false),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, RuntimeFramework.DefaultVersion),
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Net, RuntimeFramework.DefaultVersion))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Any, RuntimeFramework.DefaultVersion),
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Net, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Any, RuntimeFramework.DefaultVersion))
- .Returns(true)
- };
-
- private static readonly TestCaseData[] CanLoadData = {
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Any, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Any, new Version(2,0)))
- .Returns(true),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Any, new Version(2,0)),
- new RuntimeFramework(RuntimeType.Any, new Version(4,0)))
- .Returns(false),
- new TestCaseData(
- new RuntimeFramework(RuntimeType.Any, new Version(4,0)),
- new RuntimeFramework(RuntimeType.Any, new Version(2,0)))
- .Returns(true)
- };
-#pragma warning restore 414
-
- public struct FrameworkData
- {
- public RuntimeType runtime;
- public Version frameworkVersion;
- public Version clrVersion;
- public string representation;
- public string displayName;
-
- public FrameworkData(RuntimeType runtime, Version frameworkVersion, Version clrVersion,
- string representation, string displayName)
- {
- this.runtime = runtime;
- this.frameworkVersion = frameworkVersion;
- this.clrVersion = clrVersion;
- this.representation = representation;
- this.displayName = displayName;
- }
-
- public override string ToString()
- {
- return string.Format("<{0}-{1}>", this.runtime, this.frameworkVersion);
- }
- }
-
-#pragma warning disable 414
- static FrameworkData[] frameworkData = new FrameworkData[] {
- new FrameworkData(RuntimeType.Net, new Version(1,0), new Version(1,0,3705), "net-1.0", ".NET 1.0"),
- new FrameworkData(RuntimeType.Net, new Version(1,1), new Version(1,1,4322), "net-1.1", ".NET 1.1"),
- new FrameworkData(RuntimeType.Net, new Version(2,0), new Version(2,0,50727), "net-2.0", ".NET 2.0"),
- new FrameworkData(RuntimeType.Net, new Version(3,0), new Version(2,0,50727), "net-3.0", ".NET 3.0"),
- new FrameworkData(RuntimeType.Net, new Version(3,5), new Version(2,0,50727), "net-3.5", ".NET 3.5"),
- new FrameworkData(RuntimeType.Net, new Version(4,0), new Version(4,0,30319), "net-4.0", ".NET 4.0"),
- new FrameworkData(RuntimeType.Net, new Version(4,5), new Version(4,0,30319), "net-4.5", ".NET 4.5"),
- new FrameworkData(RuntimeType.Net, new Version(4,5,1), new Version(4,0,30319), "net-4.5.1", ".NET 4.5.1"),
- new FrameworkData(RuntimeType.Net, new Version(4,5,2), new Version(4,0,30319), "net-4.5.2", ".NET 4.5.2"),
- new FrameworkData(RuntimeType.Net, new Version(4,6), new Version(4,0,30319), "net-4.6", ".NET 4.6"),
- new FrameworkData(RuntimeType.Net, new Version(4,6,1), new Version(4,0,30319), "net-4.6.1", ".NET 4.6.1"),
- new FrameworkData(RuntimeType.Net, new Version(4,6,2), new Version(4,0,30319), "net-4.6.2", ".NET 4.6.2"),
- new FrameworkData(RuntimeType.Net, new Version(4,7), new Version(4,0,30319), "net-4.7", ".NET 4.7"),
- new FrameworkData(RuntimeType.Net, new Version(4,7,1), new Version(4,0,30319), "net-4.7.1", ".NET 4.7.1"),
- new FrameworkData(RuntimeType.Net, new Version(4,7,2), new Version(4,0,30319), "net-4.7.2", ".NET 4.7.2"),
- new FrameworkData(RuntimeType.Net, new Version(4,8), new Version(4,0,30319), "net-4.8", ".NET 4.8"),
- new FrameworkData(RuntimeType.Net, RuntimeFramework.DefaultVersion, RuntimeFramework.DefaultVersion, "net", ".NET"),
- new FrameworkData(RuntimeType.Mono, new Version(1,0), new Version(1,1,4322), "mono-1.0", "Mono 1.0"),
- new FrameworkData(RuntimeType.Mono, new Version(2,0), new Version(2,0,50727), "mono-2.0", "Mono 2.0"),
- new FrameworkData(RuntimeType.Mono, new Version(3,5), new Version(2,0,50727), "mono-3.5", "Mono 3.5"),
- new FrameworkData(RuntimeType.Mono, new Version(4,0), new Version(4,0,30319), "mono-4.0", "Mono 4.0"),
- new FrameworkData(RuntimeType.Mono, RuntimeFramework.DefaultVersion, RuntimeFramework.DefaultVersion, "mono", "Mono"),
- new FrameworkData(RuntimeType.Any, new Version(1,1), new Version(1,1,4322), "v1.1", "v1.1"),
- new FrameworkData(RuntimeType.Any, new Version(2,0), new Version(2,0,50727), "v2.0", "v2.0"),
- new FrameworkData(RuntimeType.Any, new Version(3,5), new Version(2,0,50727), "v3.5", "v3.5"),
- new FrameworkData(RuntimeType.Any, new Version(4,0), new Version(4,0,30319), "v4.0", "v4.0"),
- new FrameworkData(RuntimeType.Any, RuntimeFramework.DefaultVersion, RuntimeFramework.DefaultVersion, "any", "Any")
- };
-#pragma warning restore 414
- }
-}
-#endif
\ No newline at end of file
diff --git a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionAssembly.cs b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionAssembly.cs
index ff614bca4..29251a975 100644
--- a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionAssembly.cs
+++ b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionAssembly.cs
@@ -1,60 +1,57 @@
// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
using System;
+using System.Collections.Generic;
using System.IO;
+using System.Runtime.Versioning;
using Mono.Cecil;
-using NUnit.Engine.Internal;
+#if NETFRAMEWORK
+using NUnit.Engine.Compatibility;
+#endif
namespace NUnit.Engine.Extensibility
{
internal class ExtensionAssembly : IExtensionAssembly, IDisposable
{
+ private AssemblyDefinition _assemblyDefinition;
+
public ExtensionAssembly(string filePath, bool fromWildCard)
{
FilePath = filePath;
FromWildCard = fromWildCard;
- Assembly = GetAssemblyDefinition();
- }
-
- public string FilePath { get; }
- public bool FromWildCard { get; }
- public AssemblyDefinition Assembly { get; }
- public string AssemblyName
- {
- get { return Assembly.Name.Name; }
- }
-
- public Version AssemblyVersion
- {
- get { return Assembly.Name.Version; }
- }
-
- public ModuleDefinition MainModule
- {
- get { return Assembly.MainModule; }
- }
-
-#if NETFRAMEWORK
- public RuntimeFramework TargetFramework
- {
- get { return new RuntimeFramework(RuntimeType.Any, Assembly.GetRuntimeVersion()); }
- }
-#endif
-
- private AssemblyDefinition GetAssemblyDefinition()
- {
var resolver = new DefaultAssemblyResolver();
resolver.AddSearchDirectory(Path.GetDirectoryName(FilePath));
resolver.AddSearchDirectory(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));
var parameters = new ReaderParameters { AssemblyResolver = resolver };
- return AssemblyDefinition.ReadAssembly(FilePath, parameters);
+ _assemblyDefinition = AssemblyDefinition.ReadAssembly(FilePath, parameters);
}
- public void Dispose()
+ public string FilePath { get; }
+ public bool FromWildCard { get; }
+ public string AssemblyName => _assemblyDefinition.Name.Name;
+ public Version AssemblyVersion => _assemblyDefinition.Name.Version;
+ public IEnumerable GetTypes() => _assemblyDefinition.MainModule.GetTypes();
+
+ public FrameworkName TargetRuntime
{
- Assembly?.Dispose();
+ get
+ {
+ string frameworkName = _assemblyDefinition.GetFrameworkName();
+
+ if (frameworkName != null)
+ return new FrameworkName(frameworkName);
+
+ // We rely on TargetRuntime being available for all assemblies later than .NET 2.0
+ var runtimeVersion = _assemblyDefinition.GetRuntimeVersion();
+ var frameworkVersion = new Version(runtimeVersion.Major, runtimeVersion.Minor);
+ return new FrameworkName(FrameworkIdentifiers.NetFramework, frameworkVersion);
+ }
}
+
+
+
+ public void Dispose() => _assemblyDefinition?.Dispose();
}
}
diff --git a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionManager.cs b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionManager.cs
index 7bae84f55..f68756529 100644
--- a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionManager.cs
+++ b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionManager.cs
@@ -416,23 +416,25 @@ internal void FindExtensionsInAssembly(ExtensionAssembly assembly)
IRuntimeFramework assemblyTargetFramework = null;
#if NETFRAMEWORK
- var currentFramework = RuntimeFramework.CurrentFramework;
- assemblyTargetFramework = assembly.TargetFramework;
- if (!currentFramework.CanLoad(assemblyTargetFramework))
+ // Use special properties provided by our backport of RuntimeInformation
+ Version currentVersion = RuntimeInformation.FrameworkVersion;
+ var frameworkName = assembly.TargetRuntime;
+
+ if (frameworkName.Identifier != FrameworkIdentifiers.NetFramework || frameworkName.Version > currentVersion)
{
if (!assembly.FromWildCard)
{
- throw new NUnitEngineException($"Extension {assembly.FilePath} targets {assemblyTargetFramework.DisplayName}, which is not available.");
+ throw new NUnitEngineException($"Extension {assembly.FilePath} targets {frameworkName}, which is not available.");
}
else
{
- log.Info($"Assembly {assembly.FilePath} targets {assemblyTargetFramework.DisplayName}, which is not available. Assembly found via wildcard.");
+ log.Info($"Assembly {assembly.FilePath} targets {frameworkName}, which is not available. Assembly found via wildcard.");
return;
}
}
#endif
- foreach (var type in assembly.MainModule.GetTypes())
+ foreach (var type in assembly.GetTypes())
{
CustomAttribute extensionAttr = type.GetAttribute("NUnit.Engine.Extensibility.ExtensionAttribute");
diff --git a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionSelector.cs b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionSelector.cs
index a008f39ae..85d80e4f4 100644
--- a/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionSelector.cs
+++ b/src/NUnitEngine/nunit.engine.core/Extensibility/ExtensionSelector.cs
@@ -41,8 +41,8 @@ public static bool IsBetterVersionOf(this IExtensionAssembly first, IExtensionAs
#if NETFRAMEWORK
//Look at target runtime
- var firstTargetRuntime = first.TargetFramework.FrameworkVersion;
- var secondTargetRuntime = second.TargetFramework.FrameworkVersion;
+ var firstTargetRuntime = first.TargetRuntime.Version;
+ var secondTargetRuntime = second.TargetRuntime.Version;
if (firstTargetRuntime > secondTargetRuntime)
return true;
diff --git a/src/NUnitEngine/nunit.engine.core/Extensibility/IExtensionAssembly.cs b/src/NUnitEngine/nunit.engine.core/Extensibility/IExtensionAssembly.cs
index acbc91d39..1d5962bce 100644
--- a/src/NUnitEngine/nunit.engine.core/Extensibility/IExtensionAssembly.cs
+++ b/src/NUnitEngine/nunit.engine.core/Extensibility/IExtensionAssembly.cs
@@ -2,6 +2,12 @@
using System;
+#if NETFRAMEWORK
+using FrameworkName = NUnit.Engine.Compatibility.FrameworkName;
+#else
+using FrameworkName = System.Runtime.Versioning.FrameworkName;
+#endif
+
namespace NUnit.Engine.Extensibility
{
internal interface IExtensionAssembly
@@ -9,8 +15,6 @@ internal interface IExtensionAssembly
bool FromWildCard { get; }
string AssemblyName { get; }
Version AssemblyVersion { get; }
-#if NETFRAMEWORK
- RuntimeFramework TargetFramework { get; }
-#endif
+ FrameworkName TargetRuntime { get; }
}
}
diff --git a/src/NUnitEngine/nunit.engine.core/FrameworkIdentifers.cs b/src/NUnitEngine/nunit.engine.core/FrameworkIdentifers.cs
new file mode 100644
index 000000000..23101ec09
--- /dev/null
+++ b/src/NUnitEngine/nunit.engine.core/FrameworkIdentifers.cs
@@ -0,0 +1,11 @@
+// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
+
+namespace NUnit.Engine
+{
+ public static class FrameworkIdentifiers
+ {
+ public const string NetFramework = ".NETFramework";
+ public const string NetCoreApp = ".NETCoreApp";
+ public const string NetStandard = ".NETStandard";
+ }
+}
diff --git a/src/NUnitEngine/nunit.engine.core/Internal/Backports/RuntimeInformation.cs b/src/NUnitEngine/nunit.engine.core/Internal/Backports/RuntimeInformation.cs
new file mode 100644
index 000000000..cd3dedf3d
--- /dev/null
+++ b/src/NUnitEngine/nunit.engine.core/Internal/Backports/RuntimeInformation.cs
@@ -0,0 +1,108 @@
+// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
+
+#if NETFRAMEWORK
+using System;
+using System.IO;
+using Microsoft.Win32;
+
+namespace NUnit.Engine.Internal.Backports
+{
+ ///
+ /// Partially replaces System.Runtime.InteropServices.RuntimeInformation
+ /// under the .NET Framework. Only FrameworkDescription is implemented.
+ ///
+ public static class RuntimeInformation
+ {
+ static RuntimeInformation()
+ {
+ bool isMono = Type.GetType("Mono.Runtime", false) != null;
+
+ Version version = new Version(Environment.Version.Major, Environment.Version.Minor);
+ if (isMono)
+ {
+ FrameworkName = "Mono";
+
+ switch (version.Major)
+ {
+ case 1:
+ version = new Version(1, 0);
+ break;
+ case 2:
+ version = new Version(3, 5);
+ break;
+ }
+ }
+ else /* We must be on Windows, so registry is available */
+ {
+ FrameworkName = ".NET Framework";
+
+ switch (version.Major)
+ {
+ case 2:
+ RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework");
+ if (key != null)
+ {
+ string installRoot = key.GetValue("InstallRoot") as string;
+ if (installRoot != null)
+ {
+ if (Directory.Exists(System.IO.Path.Combine(installRoot, "v3.5")))
+ {
+ version = new Version(3, 5);
+ }
+ else if (Directory.Exists(System.IO.Path.Combine(installRoot, "v3.0")))
+ {
+ version = new Version(3, 0);
+ }
+ }
+ }
+ break;
+ case 4:
+ key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full");
+ if (key != null)
+ {
+ version = new Version(4, 5);
+ int release = (int)key.GetValue("Release", 0);
+ foreach (var entry in ReleaseTable)
+ if (release >= entry.Release)
+ version = entry.Version;
+ }
+ break;
+ }
+ }
+
+ FrameworkVersion = version;
+ }
+
+ public static string FrameworkName { get; }
+ public static Version FrameworkVersion { get; }
+ public static string FrameworkDescription => $"{FrameworkName} {FrameworkVersion}";
+
+
+ private struct MinimumRelease
+ {
+ public readonly int Release;
+ public readonly Version Version;
+
+ public MinimumRelease(int release, Version version)
+ {
+ Release = release;
+ Version = version;
+ }
+ }
+
+ private static readonly MinimumRelease[] ReleaseTable = new MinimumRelease[]
+ {
+ new MinimumRelease(378389, new Version(4, 5)),
+ new MinimumRelease(378675, new Version(4, 5, 1)),
+ new MinimumRelease(379893, new Version(4, 5, 2)),
+ new MinimumRelease(393295, new Version(4, 6)),
+ new MinimumRelease(394254, new Version(4, 6, 1)),
+ new MinimumRelease(394802, new Version(4, 6, 2)),
+ new MinimumRelease(460798, new Version(4, 7)),
+ new MinimumRelease(461308, new Version(4, 7, 1)),
+ new MinimumRelease(461808, new Version(4, 7, 2)),
+ new MinimumRelease(528040, new Version(4, 8))
+ };
+ }
+}
+#endif
diff --git a/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs b/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs
deleted file mode 100644
index 33fc7116b..000000000
--- a/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs
+++ /dev/null
@@ -1,746 +0,0 @@
-// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
-
-#if NETFRAMEWORK
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Reflection;
-using Microsoft.Win32;
-using NUnit.Engine.Internal.RuntimeFrameworks;
-
-namespace NUnit.Engine
-{
- ///
- /// RuntimeFramework represents a particular version
- /// of a common language runtime implementation.
- ///
- [Serializable]
- public sealed class RuntimeFramework : IRuntimeFramework
- {
- // TODO: RuntimeFramework was originally created for use in
- // a single-threaded environment. The introduction of parallel
- // execution and especially parallel loading of tests has
- // exposed some weaknesses.
- //
- // Ideally, we should remove all knowledge of the environment
- // from RuntimeFramework. An instance of RuntimeFramework does
- // not need to know, for example, if it is available on the
- // current system. In the present architecture, that's really
- // the job of the RuntimeFrameworkService. Other functions
- // may actually belong in TestAgency.
- //
- // All the static properties of RuntimeFramework need to be
- // examined for thread-safety, particularly CurrentFramework
- // and AvailableFrameworks. The latter caused a problem with
- // parallel loading, which has been fixed for now through a
- // hack added to RuntimeFrameworkService. We may be able to
- // move all this functionality to services, eliminating the
- // use of public static properties here.
-
- ///
- /// DefaultVersion is an empty Version, used to indicate that
- /// NUnit should select the CLR version to use for the test.
- ///
- public static readonly Version DefaultVersion = new Version(0, 0);
-
- #region IRuntimeFramework Implementation
-
- ///
- /// Gets the unique Id for this runtime, such as "net-4.5"
- ///
- public string Id
- {
- get
- {
- if (FrameworkVersion == DefaultVersion)
- {
- return Runtime.ToString().ToLower();
- }
- else
- {
- string vstring = FrameworkVersion.ToString();
- if (Runtime == RuntimeType.Any)
- return "v" + vstring;
- else
- return Runtime.ToString().ToLower() + "-" + vstring;
- }
- }
- }
-
- ///
- /// The type of this runtime framework
- ///
- public RuntimeType Runtime { get; private set; }
-
- ///
- /// The framework version for this runtime framework
- ///
- public Version FrameworkVersion { get; private set; }
-
- ///
- /// The CLR version for this runtime framework
- ///
- public Version ClrVersion { get; private set; }
-
- ///
- /// The Profile for this framework, where relevant.
- /// May be null and will have different sets of
- /// values for each Runtime.
- ///
- public string Profile { get; private set; }
-
- ///
- /// Returns the Display name for this framework
- ///
- public string DisplayName { get; private set; }
-
- #endregion
-
- private static RuntimeFramework _currentFramework;
- private static List _availableFrameworks;
-
- private static readonly string DEFAULT_WINDOWS_MONO_DIR =
- Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Mono");
-
- ///
- /// Construct from a runtime type and version. If the version has
- /// two parts, it is taken as a framework version. If it has three
- /// or more, it is taken as a CLR version. In either case, the other
- /// version is deduced based on the runtime type and provided version.
- ///
- /// The runtime type of the framework
- /// The version of the framework
- public RuntimeFramework(RuntimeType runtime, Version version)
- : this(runtime, version, null)
- {
- }
-
- ///
- /// Construct from a runtime type, version and profile. The version
- /// may be either a framework version or a CLR version. If a CLR
- /// version is provided, we try to deduce the framework version but
- /// this may not always be successful, in which case a version of
- /// 0.0 is used.
- ///
- /// The runtime type of the framework.
- /// The version of the framework.
- /// The profile of the framework. Null if unspecified.
- public RuntimeFramework(RuntimeType runtime, Version version, string profile)
- {
- Runtime = runtime;
- FrameworkVersion = ClrVersion = version;
-
- // Version 0.0 means any version so we can't deduce anything
- if (version != DefaultVersion)
- {
- if (IsFrameworkVersion(version))
- ClrVersion = GetClrVersionForFramework(version);
- else
- FrameworkVersion = GetFrameworkVersionForClr(version);
- }
-
- Profile = profile;
-
- DisplayName = GetDefaultDisplayName(runtime, FrameworkVersion, profile);
- }
-
- private bool IsFrameworkVersion(Version v)
- {
- // All known framework versions have either two components or
- // three. If three, then the Build is currently less than 3.
- return v.Build < 3 && v.Revision == -1;
- }
-
- private Version GetClrVersionForFramework(Version frameworkVersion)
- {
- switch (Runtime)
- {
- case RuntimeType.Net:
- case RuntimeType.Any:
- switch (frameworkVersion.Major)
- {
- case 1:
- switch (frameworkVersion.Minor)
- {
- case 0:
- return new Version(1, 0, 3705);
- case 1:
- return new Version(1, 1, 4322);
- }
- break;
- case 2:
- case 3:
- return new Version(2, 0, 50727);
- case 4:
- return new Version(4, 0, 30319);
- }
- break;
- case RuntimeType.Mono:
- switch (frameworkVersion.Major)
- {
- case 1:
- return new Version(1, 1, 4322);
- case 2:
- case 3:
- return new Version(2, 0, 50727);
- case 4:
- return new Version(4, 0, 30319);
- }
- break;
- case RuntimeType.NetCore:
- switch (frameworkVersion.Major)
- {
- case 1:
- case 2:
- // For pre-3.0 versions of .NET Core, Environment.Version returns 4.0.30319.42000
- return new Version(4, 0, 30319);
- case 3:
- return new Version(3, 1, 10);
- case 5:
- return new Version(5, 0, 1);
- case 6:
- return new Version(6, 0, 0);
- }
- break;
- }
-
- throw new ArgumentException("Unknown framework version " + frameworkVersion.ToString(), "version");
- }
-
- private Version GetFrameworkVersionForClr(Version clrVersion)
- {
- return Runtime == RuntimeType.Mono && clrVersion.Major == 1
- ? new Version(1, 0)
- : new Version(clrVersion.Major, clrVersion.Minor);
- }
-
- ///
- /// Static method to return a RuntimeFramework object
- /// for the framework that is currently in use.
- ///
- public static RuntimeFramework CurrentFramework
- {
- get
- {
- if (_currentFramework == null)
- {
- Type monoRuntimeType = Type.GetType("Mono.Runtime", false);
- bool isMono = monoRuntimeType != null;
-
- RuntimeType runtime = isMono
- ? RuntimeType.Mono
- : RuntimeType.Net;
-
- int major = Environment.Version.Major;
- int minor = Environment.Version.Minor;
-
- if (isMono)
- {
- switch (major)
- {
- case 1:
- minor = 0;
- break;
- case 2:
- major = 3;
- minor = 5;
- break;
- }
- }
- else /* It's windows */
- if (major == 2)
- {
- RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework");
- if (key != null)
- {
- string installRoot = key.GetValue("InstallRoot") as string;
- if (installRoot != null)
- {
- if (Directory.Exists(Path.Combine(installRoot, "v3.5")))
- {
- major = 3;
- minor = 5;
- }
- else if (Directory.Exists(Path.Combine(installRoot, "v3.0")))
- {
- major = 3;
- minor = 0;
- }
- }
- }
- }
- else if (major == 4 && Type.GetType("System.Reflection.AssemblyMetadataAttribute") != null)
- {
- minor = 5;
- }
-
- _currentFramework = new RuntimeFramework(runtime, new Version(major, minor));
- _currentFramework.ClrVersion = Environment.Version;
-
- if (isMono)
- {
- if (MonoPrefix == null)
- MonoPrefix = GetMonoPrefixFromAssembly(monoRuntimeType.Assembly);
-
- MethodInfo getDisplayNameMethod = monoRuntimeType.GetMethod(
- "GetDisplayName", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.ExactBinding);
- if (getDisplayNameMethod != null)
- {
- string displayName = (string)getDisplayNameMethod.Invoke(null, new object[0]);
-
- int space = displayName.IndexOf(' ');
- if (space >= 3) // Minimum length of a version
- {
- string version = displayName.Substring(0, space);
- displayName = "Mono " + version;
- if (MonoVersion == null)
- MonoVersion = new Version(version);
- }
- else
- displayName = "Mono " + displayName;
-
- _currentFramework.DisplayName = displayName;
- }
- }
- }
-
- return _currentFramework;
- }
- }
-
- ///
- /// Gets an array of all available frameworks
- ///
- public static RuntimeFramework[] AvailableFrameworks
- {
- get
- {
- if (_availableFrameworks == null)
- FindAvailableFrameworks();
-
- return _availableFrameworks.ToArray();
- }
- }
-
- ///
- /// The version of Mono in use or null if no Mono runtime
- /// is available on this machine.
- ///
- /// The mono version.
- private static Version MonoVersion { get; set; }
-
- ///
- /// The install directory where the version of mono in
- /// use is located. Null if no Mono runtime is present.
- ///
- private static string MonoPrefix { get; set; }
-
- ///
- /// The path to the mono executable, based on the
- /// Mono prefix if available. Otherwise, uses "mono",
- /// to invoke a script of that name.
- ///
- public static string MonoExePath
- {
- get
- {
- return MonoPrefix != null && Environment.OSVersion.Platform == PlatformID.Win32NT
- ? Path.Combine(MonoPrefix, "bin/mono.exe")
- : "mono";
- }
- }
-
- ///
- /// Returns true if the current RuntimeFramework is available.
- /// In the current implementation, only Mono and Microsoft .NET
- /// are supported.
- ///
- /// True if it's available, false if not
- public bool IsAvailable
- {
- get
- {
- foreach (RuntimeFramework framework in AvailableFrameworks)
- if (framework.Supports(this))
- return true;
-
- return false;
- }
- }
-
- ///
- /// Parses a string representing a RuntimeFramework.
- /// The string may be just a RuntimeType name or just
- /// a Version or a hyphenated RuntimeType-Version or
- /// a Version prefixed by 'v'.
- ///
- ///
- ///
- public static RuntimeFramework Parse(string s)
- {
- RuntimeType runtime = RuntimeType.Any;
- Version version = DefaultVersion;
-
- string[] parts = s.Split(new char[] { '-' });
- if (parts.Length == 2)
- {
- runtime = (RuntimeType)System.Enum.Parse(typeof(RuntimeType), parts[0], true);
- string vstring = parts[1];
- if (vstring != "")
- version = new Version(vstring);
- }
- else if (char.ToLower(s[0]) == 'v')
- {
- version = new Version(s.Substring(1));
- }
- else if (IsRuntimeTypeName(s))
- {
- runtime = (RuntimeType)System.Enum.Parse(typeof(RuntimeType), s, true);
- }
- else
- {
- version = new Version(s);
- }
-
- return new RuntimeFramework(runtime, version);
- }
-
- public static bool TryParse(string s, out RuntimeFramework runtimeFramework)
- {
- try
- {
- runtimeFramework = Parse(s);
- return true;
- }
- catch
- {
- runtimeFramework = null;
- return false;
- }
- }
-
- ///
- /// Overridden to return the short name of the framework
- ///
- ///
- public override string ToString()
- {
- return this.Id;
- }
-
- ///
- /// Returns true if the current framework matches the
- /// one supplied as an argument. Both the RuntimeType
- /// and the version must match.
- ///
- /// Two RuntimeTypes match if they are equal, if either one
- /// is RuntimeType.Any or if one is RuntimeType.Net and
- /// the other is RuntimeType.Mono.
- ///
- /// Two versions match if all specified version components
- /// are equal. Negative (i.e. unspecified) version
- /// components are ignored.
- ///
- /// The RuntimeFramework to be matched.
- /// true on match, otherwise false
- public bool Supports(RuntimeFramework target)
- {
- if (!this.Supports(target.Runtime))
- return false;
-
- if (FrameworkVersion == DefaultVersion || target.FrameworkVersion == DefaultVersion)
- return true;
-
- return VersionsMatch(this.ClrVersion, target.ClrVersion)
- && this.FrameworkVersion.Major >= target.FrameworkVersion.Major
- && this.FrameworkVersion.Minor >= target.FrameworkVersion.Minor;
- }
-
- private bool Supports(RuntimeType targetRuntime)
- {
- if (this.Runtime == targetRuntime)
- return true;
-
- if (this.Runtime == RuntimeType.Any || targetRuntime == RuntimeType.Any)
- return true;
-
- if (this.Runtime == RuntimeType.Net && targetRuntime == RuntimeType.Mono)
- return true;
-
- if (this.Runtime == RuntimeType.Mono && targetRuntime == RuntimeType.Net)
- return true;
-
- return false;
- }
-
- public bool CanLoad(IRuntimeFramework requested)
- {
- return FrameworkVersion >= requested.FrameworkVersion;
- }
-
- private static bool IsRuntimeTypeName(string name)
- {
- foreach (string item in Enum.GetNames(typeof(RuntimeType)))
- if (item.ToLower() == name.ToLower())
- return true;
-
- return false;
- }
-
- private static string GetDefaultDisplayName(RuntimeType runtime, Version version, string profile)
- {
- string displayName;
-
- if (version == DefaultVersion)
- displayName = GetRuntimeDisplayName(runtime);
- else if (runtime == RuntimeType.Any)
- displayName = "v" + version.ToString();
- else
- displayName = GetRuntimeDisplayName(runtime) + " " + version.ToString();
-
- if (!string.IsNullOrEmpty(profile) && profile != "Full")
- displayName += " - " + profile;
-
- return displayName;
- }
-
- private static string GetRuntimeDisplayName(RuntimeType runtime)
- {
- switch (runtime)
- {
- case RuntimeType.Net:
- return ".NET";
- case RuntimeType.NetCore:
- return ".NETCore";
- default:
- return runtime.ToString();
- }
- }
-
- private static bool VersionsMatch(Version v1, Version v2)
- {
- return v1.Major == v2.Major &&
- v1.Minor == v2.Minor &&
- (v1.Build < 0 || v2.Build < 0 || v1.Build == v2.Build) &&
- (v1.Revision < 0 || v2.Revision < 0 || v1.Revision == v2.Revision);
- }
-
- private static string GetMonoPrefixFromAssembly(Assembly assembly)
- {
- string prefix = assembly.Location;
-
- // In all normal mono installations, there will be sufficient
- // levels to complete the four iterations. But just in case
- // files have been copied to some non-standard place, we check.
- for (int i = 0; i < 4; i++)
- {
- string dir = Path.GetDirectoryName(prefix);
- if (string.IsNullOrEmpty(dir)) break;
-
- prefix = dir;
- }
-
- return prefix;
- }
-
- private static void FindAvailableFrameworks()
- {
- _availableFrameworks = new List();
-
- if (Environment.OSVersion.Platform == PlatformID.Win32NT)
- _availableFrameworks.AddRange(DotNetFrameworkLocator.FindDotNetFrameworks());
-
- FindDefaultMonoFramework();
- FindDotNetCoreFrameworks();
- }
-
- private static void FindDefaultMonoFramework()
- {
- if (CurrentFramework.Runtime == RuntimeType.Mono)
- UseCurrentMonoFramework();
- else
- if (Environment.OSVersion.Platform == PlatformID.Win32NT)
- FindBestMonoFrameworkOnWindows();
- }
-
- private static void UseCurrentMonoFramework()
- {
- Debug.Assert(CurrentFramework.Runtime == RuntimeType.Mono && MonoPrefix != null && MonoVersion != null);
-
- // Multiple profiles are no longer supported with Mono 4.0
- if (MonoVersion.Major < 4 && FindAllMonoProfiles() > 0)
- return;
-
- // If Mono 4.0+ or no profiles found, just use current runtime
- _availableFrameworks.Add(RuntimeFramework.CurrentFramework);
- }
-
- private static void FindBestMonoFrameworkOnWindows()
- {
- // First, look for recent frameworks that use the Software\Mono Key
- RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\Mono");
-
- if (key != null && (int)key.GetValue("Installed", 0) == 1)
- {
- string version = key.GetValue("Version") as string;
- MonoPrefix = key.GetValue("SdkInstallRoot") as string;
-
- if (version != null)
- {
- MonoVersion = new Version(version);
- AddMonoFramework(new Version(4, 5), null);
- return;
- }
- }
-
- // Some later 3.x Mono releases didn't use the registry
- // so check in standard location for them.
- if (Directory.Exists(DEFAULT_WINDOWS_MONO_DIR))
- {
- MonoPrefix = DEFAULT_WINDOWS_MONO_DIR;
- AddMonoFramework(new Version(4, 5), null);
- return;
- }
-
- // Look in the Software\Novell key for older versions
- key = Registry.LocalMachine.OpenSubKey(@"Software\Novell\Mono");
- if (key != null)
- {
- string version = key.GetValue("DefaultCLR") as string;
- if (version != null)
- {
- RegistryKey subKey = key.OpenSubKey(version);
- if (subKey != null)
- {
- MonoPrefix = subKey.GetValue("SdkInstallRoot") as string;
- MonoVersion = new Version(version);
-
- FindAllMonoProfiles();
- }
- }
- }
- }
-
- private static int FindAllMonoProfiles()
- {
- int count = 0;
-
- if (MonoPrefix != null)
- {
- if (File.Exists(Path.Combine(MonoPrefix, "lib/mono/1.0/mscorlib.dll")))
- {
- AddMonoFramework(new Version(1, 1, 4322), "1.0");
- count++;
- }
-
- if (File.Exists(Path.Combine(MonoPrefix, "lib/mono/2.0/mscorlib.dll")))
- {
- AddMonoFramework(new Version(2, 0), "2.0");
- count++;
- }
-
- if (Directory.Exists(Path.Combine(MonoPrefix, "lib/mono/3.5")))
- {
- AddMonoFramework(new Version(3, 5), "3.5");
- count++;
- }
-
- if (File.Exists(Path.Combine(MonoPrefix, "lib/mono/4.0/mscorlib.dll")))
- {
- AddMonoFramework(new Version(4, 0), "4.0");
- count++;
- }
-
- if (File.Exists(Path.Combine(MonoPrefix, "lib/mono/4.5/mscorlib.dll")))
- {
- AddMonoFramework(new Version(4, 5), "4.5");
- count++;
- }
- }
-
- return count;
- }
-
- private static void AddMonoFramework(Version frameworkVersion, string profile)
- {
- var framework = new RuntimeFramework(RuntimeType.Mono, frameworkVersion)
- {
- Profile = profile,
- DisplayName = MonoVersion != null
- ? "Mono " + MonoVersion.ToString() + " - " + profile + " Profile"
- : "Mono - " + profile + " Profile"
- };
-
- _availableFrameworks.Add(framework);
- }
-
- private static void FindDotNetCoreFrameworks()
- {
- const string WINDOWS_INSTALL_DIR = "C:\\Program Files\\dotnet\\";
- const string LINUX_INSTALL_DIR = "/usr/shared/dotnet/";
- string INSTALL_DIR = Path.DirectorySeparatorChar == '\\'
- ? WINDOWS_INSTALL_DIR
- : LINUX_INSTALL_DIR;
-
- if (!Directory.Exists(INSTALL_DIR))
- return;
- if (!File.Exists(Path.Combine(INSTALL_DIR, "dotnet.exe")))
- return;
-
- string runtimeDir = Path.Combine(INSTALL_DIR, Path.Combine("shared", "Microsoft.NETCore.App"));
- if (!Directory.Exists(runtimeDir))
- return;
-
- var dirList = new DirectoryInfo(runtimeDir).GetDirectories();
- var dirNames = new List();
- foreach (var dir in dirList)
- dirNames.Add(dir.Name);
- var runtimes = GetNetCoreRuntimesFromDirectoryNames(dirNames);
-
- _availableFrameworks.AddRange(runtimes);
- }
-
- // Deal with oddly named directories, which may sometimes appear when previews are installed
- internal static IList GetNetCoreRuntimesFromDirectoryNames(IEnumerable dirNames)
- {
- const string VERSION_CHARS = ".0123456789";
- var runtimes = new List();
-
- foreach (string dirName in dirNames)
- {
- int len = 0;
- foreach (char c in dirName)
- {
- if (VERSION_CHARS.IndexOf(c) >= 0)
- len++;
- else
- break;
- }
-
- if (len == 0)
- continue;
-
- Version fullVersion = null;
- try
- {
- fullVersion = new Version(dirName.Substring(0, len));
- }
- catch
- {
- continue;
- }
-
- var newVersion = new Version(fullVersion.Major, fullVersion.Minor);
- int count = runtimes.Count;
- if (count > 0 && runtimes[count - 1].FrameworkVersion == newVersion)
- continue;
-
- runtimes.Add(new RuntimeFramework(RuntimeType.NetCore, newVersion));
- }
-
- return runtimes;
- }
- }
-}
-#endif
diff --git a/src/NUnitEngine/nunit.engine.core/RuntimeType.cs b/src/NUnitEngine/nunit.engine.core/RuntimeType.cs
deleted file mode 100644
index 0594b6641..000000000
--- a/src/NUnitEngine/nunit.engine.core/RuntimeType.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
-
-namespace NUnit.Engine
-{
- ///
- /// Enumeration identifying a common language
- /// runtime implementation.
- ///
- public enum RuntimeType
- {
- /// Any supported runtime framework
- Any,
- /// Microsoft .NET Framework
- Net,
- /// Mono
- Mono,
- /// .NET Core
- NetCore
- }
-}
diff --git a/src/NUnitEngine/nunit.engine.tests/RuntimeFrameworkTests.cs b/src/NUnitEngine/nunit.engine.tests/RuntimeFrameworkTests.cs
new file mode 100644
index 000000000..d91f62b82
--- /dev/null
+++ b/src/NUnitEngine/nunit.engine.tests/RuntimeFrameworkTests.cs
@@ -0,0 +1,160 @@
+// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
+
+#if NETFRAMEWORK
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+
+namespace NUnit.Engine
+{
+ [TestFixture]
+ public class RuntimeFrameworkTests
+ {
+ [TestCaseSource(nameof(frameworkData))]
+ public void CanCreateUsingFrameworkVersion(FrameworkData data)
+ {
+ RuntimeFramework framework = new RuntimeFramework(data.runtime, data.frameworkVersion);
+ Assert.That(framework.Runtime, Is.EqualTo(data.runtime));
+ Assert.That(framework.FrameworkVersion, Is.EqualTo(data.frameworkVersion));
+ Assert.That(framework.ClrVersion, Is.EqualTo(data.clrVersion));
+ }
+
+ [TestCaseSource(nameof(frameworkData))]
+ public void CanParseRuntimeFramework(FrameworkData data)
+ {
+ RuntimeFramework framework = RuntimeFramework.Parse(data.representation);
+ Assert.That(framework.Runtime, Is.EqualTo(data.runtime));
+ Assert.That(framework.ClrVersion, Is.EqualTo(data.clrVersion));
+ }
+
+ [TestCaseSource(nameof(frameworkData))]
+ public void CanDisplayFrameworkAsString(FrameworkData data)
+ {
+ RuntimeFramework framework = new RuntimeFramework(data.runtime, data.frameworkVersion);
+ Assert.That(framework.ToString(), Is.EqualTo(data.representation));
+ Assert.That(framework.DisplayName, Is.EqualTo(data.displayName));
+ }
+
+ [TestCaseSource(nameof(matchData))]
+ public bool CanMatchRuntimes(RuntimeFramework f1, RuntimeFramework f2)
+ {
+ return f1.Supports(f2);
+ }
+
+ [TestCaseSource(nameof(CanLoadData))]
+ public bool CanLoad(RuntimeFramework f1, RuntimeFramework f2)
+ {
+ return f1.CanLoad(f2);
+ }
+
+#pragma warning disable 414
+ static TestCaseData[] matchData = new TestCaseData[] {
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Net, new Version(3,5)),
+ new RuntimeFramework(Runtime.Net, new Version(2,0)))
+ .Returns(true),
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Net, new Version(2,0)),
+ new RuntimeFramework(Runtime.Net, new Version(3,5)))
+ .Returns(false),
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Net, new Version(3,5)),
+ new RuntimeFramework(Runtime.Net, new Version(3,5)))
+ .Returns(true),
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Net, new Version(2,0)),
+ new RuntimeFramework(Runtime.Net, new Version(2,0)))
+ .Returns(true),
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Net, new Version(2,0)),
+ new RuntimeFramework(Runtime.Mono, new Version(2,0)))
+ .Returns(true),
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Mono, new Version(2,0)),
+ new RuntimeFramework(Runtime.Net, new Version(2,0)))
+ .Returns(true),
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Net, new Version(4,0)),
+ new RuntimeFramework(Runtime.Mono, new Version(4,0)))
+ .Returns(true),
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Mono, new Version(4,0)),
+ new RuntimeFramework(Runtime.Net, new Version(4,0)))
+ .Returns(true),
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Net, new Version(2,0)),
+ new RuntimeFramework(Runtime.Net, new Version(1,1)))
+ .Returns(false),
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Mono, new Version(1,1)), // non-existent version but it works
+ new RuntimeFramework(Runtime.Mono, new Version(1,0)))
+ .Returns(true),
+ };
+
+ private static readonly TestCaseData[] CanLoadData = {
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Net, new Version(2,0)),
+ new RuntimeFramework(Runtime.Net, new Version(2,0)))
+ .Returns(true),
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Net, new Version(2,0)),
+ new RuntimeFramework(Runtime.Net, new Version(4,0)))
+ .Returns(false),
+ new TestCaseData(
+ new RuntimeFramework(Runtime.Net, new Version(4,0)),
+ new RuntimeFramework(Runtime.Net, new Version(2,0)))
+ .Returns(true)
+ };
+#pragma warning restore 414
+
+ public struct FrameworkData
+ {
+ public Runtime runtime;
+ public Version frameworkVersion;
+ public Version clrVersion;
+ public string representation;
+ public string displayName;
+
+ public FrameworkData(Runtime runtime, Version frameworkVersion, Version clrVersion,
+ string representation, string displayName)
+ {
+ this.runtime = runtime;
+ this.frameworkVersion = frameworkVersion;
+ this.clrVersion = clrVersion;
+ this.representation = representation;
+ this.displayName = displayName;
+ }
+
+ public override string ToString()
+ {
+ return string.Format("<{0}-{1}>", this.runtime, this.frameworkVersion);
+ }
+ }
+
+#pragma warning disable 414
+ static FrameworkData[] frameworkData = new FrameworkData[] {
+ new FrameworkData(Runtime.Net, new Version(1,0), new Version(1,0,3705), "net-1.0", ".NET 1.0"),
+ new FrameworkData(Runtime.Net, new Version(1,1), new Version(1,1,4322), "net-1.1", ".NET 1.1"),
+ new FrameworkData(Runtime.Net, new Version(2,0), new Version(2,0,50727), "net-2.0", ".NET 2.0"),
+ new FrameworkData(Runtime.Net, new Version(3,0), new Version(2,0,50727), "net-3.0", ".NET 3.0"),
+ new FrameworkData(Runtime.Net, new Version(3,5), new Version(2,0,50727), "net-3.5", ".NET 3.5"),
+ new FrameworkData(Runtime.Net, new Version(4,0), new Version(4,0,30319), "net-4.0", ".NET 4.0"),
+ new FrameworkData(Runtime.Net, new Version(4,5), new Version(4,0,30319), "net-4.5", ".NET 4.5"),
+ new FrameworkData(Runtime.Net, new Version(4,5,1), new Version(4,0,30319), "net-4.5.1", ".NET 4.5.1"),
+ new FrameworkData(Runtime.Net, new Version(4,5,2), new Version(4,0,30319), "net-4.5.2", ".NET 4.5.2"),
+ new FrameworkData(Runtime.Net, new Version(4,6), new Version(4,0,30319), "net-4.6", ".NET 4.6"),
+ new FrameworkData(Runtime.Net, new Version(4,6,1), new Version(4,0,30319), "net-4.6.1", ".NET 4.6.1"),
+ new FrameworkData(Runtime.Net, new Version(4,6,2), new Version(4,0,30319), "net-4.6.2", ".NET 4.6.2"),
+ new FrameworkData(Runtime.Net, new Version(4,7), new Version(4,0,30319), "net-4.7", ".NET 4.7"),
+ new FrameworkData(Runtime.Net, new Version(4,7,1), new Version(4,0,30319), "net-4.7.1", ".NET 4.7.1"),
+ new FrameworkData(Runtime.Net, new Version(4,7,2), new Version(4,0,30319), "net-4.7.2", ".NET 4.7.2"),
+ new FrameworkData(Runtime.Net, new Version(4,8), new Version(4,0,30319), "net-4.8", ".NET 4.8"),
+ new FrameworkData(Runtime.Mono, new Version(1,0), new Version(1,1,4322), "mono-1.0", "Mono 1.0"),
+ new FrameworkData(Runtime.Mono, new Version(2,0), new Version(2,0,50727), "mono-2.0", "Mono 2.0"),
+ new FrameworkData(Runtime.Mono, new Version(3,5), new Version(2,0,50727), "mono-3.5", "Mono 3.5"),
+ new FrameworkData(Runtime.Mono, new Version(4,0), new Version(4,0,30319), "mono-4.0", "Mono 4.0"),
+ };
+#pragma warning restore 414
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/NUnitEngine/nunit.engine.tests/Services/AgentProcessTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/AgentProcessTests.cs
index b5b4ed323..2b0c9162a 100644
--- a/src/NUnitEngine/nunit.engine.tests/Services/AgentProcessTests.cs
+++ b/src/NUnitEngine/nunit.engine.tests/Services/AgentProcessTests.cs
@@ -72,11 +72,11 @@ public void DefaultValues(string framework)
Assert.False(startInfo.LoadUserProfile, "LoadUserProfile");
var targetRuntime = RuntimeFramework.Parse(framework);
- if (targetRuntime.Runtime == RuntimeType.Mono)
+ if (targetRuntime.Runtime == Runtime.Mono)
{
string monoOptions = "--runtime=v" + targetRuntime.ClrVersion.ToString(3);
monoOptions += " --debug";
- Assert.That(startInfo.FileName, Is.EqualTo(RuntimeFramework.MonoExePath));
+ Assert.That(startInfo.FileName, Is.EqualTo(RuntimeFrameworkService.MonoExePath));
Assert.That(startInfo.Arguments, Is.EqualTo(
$"{monoOptions} \"{process.AgentExePath}\" {process.AgentArgs}"));
}
diff --git a/src/NUnitEngine/nunit.engine.tests/Services/FakeRuntimeService.cs b/src/NUnitEngine/nunit.engine.tests/Services/FakeRuntimeService.cs
index 1cb3579d3..a5b745c71 100644
--- a/src/NUnitEngine/nunit.engine.tests/Services/FakeRuntimeService.cs
+++ b/src/NUnitEngine/nunit.engine.tests/Services/FakeRuntimeService.cs
@@ -1,9 +1,13 @@
// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
+using System.Collections.Generic;
+
namespace NUnit.Engine.Services
{
- public class FakeRuntimeService : FakeService, IRuntimeFrameworkService
+ public class FakeRuntimeService : FakeService, IRuntimeFrameworkService, IAvailableRuntimes
{
+ public IRuntimeFramework CurrentFramework => throw new System.NotImplementedException();
+
bool IRuntimeFrameworkService.IsAvailable(string framework)
{
return true;
@@ -13,5 +17,7 @@ string IRuntimeFrameworkService.SelectRuntimeFramework(TestPackage package)
{
return string.Empty;
}
+
+ public IList AvailableRuntimes => throw new System.NotImplementedException();
}
}
diff --git a/src/NUnitEngine/nunit.engine.tests/Services/RuntimeFrameworkServiceTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/RuntimeFrameworkServiceTests.cs
index c0f938387..12000790d 100644
--- a/src/NUnitEngine/nunit.engine.tests/Services/RuntimeFrameworkServiceTests.cs
+++ b/src/NUnitEngine/nunit.engine.tests/Services/RuntimeFrameworkServiceTests.cs
@@ -13,6 +13,16 @@ public class RuntimeFrameworkServiceTests
{
private RuntimeFrameworkService _runtimeService;
+ // We can do this because we currently only build under NETFRAMEWORK
+ private static Runtime _currentRuntime =
+ Type.GetType("Mono.Runtime", false) != null
+ ? Runtime.Mono
+ : Runtime.Net;
+
+ // TODO: We cast IRuntimeFramework to RuntimeFramework in several
+ // places here. Ideally, we should deal with the interfaces but
+ // they would need to be changed to do that. For now, the casts are
+ // used since we may end up eliminating the RuntimeFramework class.
[SetUp]
public void CreateServiceContext()
{
@@ -47,6 +57,21 @@ public void SelectRuntimeFramework(string assemblyName, bool runAsX86)
Assert.That(package.GetSetting("RunAsX86", false), Is.EqualTo(runAsX86));
}
+ [Test]
+ public void CanGetCurrentFramework()
+ {
+ var framework = _runtimeService.CurrentFramework as RuntimeFramework;
+
+ Assert.That(framework.Runtime, Is.EqualTo(_currentRuntime));
+ Assert.That(framework.ClrVersion, Is.EqualTo(Environment.Version));
+ }
+
+ [Test]
+ public void CurrentFrameworkHasBuildSpecified()
+ {
+ Assert.That(_runtimeService.CurrentFramework.ClrVersion.Build, Is.GreaterThan(0));
+ }
+
[Test]
public void AvailableFrameworks()
{
@@ -56,6 +81,34 @@ public void AvailableFrameworks()
Console.WriteLine("Available: {0}", framework.DisplayName);
}
+ [Test]
+ public void CurrentFrameworkMustBeAvailable()
+ {
+ var current = _runtimeService.CurrentFramework;
+ Console.WriteLine("Current framework is {0} ({1})", current.DisplayName, current.Id);
+ Assert.That(_runtimeService.IsAvailable(current.Id), "{0} not available", current);
+ }
+
+ [Test]
+ public void AvailableFrameworksList_IncludesCurrentFramework()
+ {
+ var current = _runtimeService.CurrentFramework as RuntimeFramework;
+ foreach (var framework in _runtimeService.AvailableRuntimes)
+ if (current.Supports(framework as RuntimeFramework))
+ return;
+
+ Assert.Fail("CurrentFramework not listed as available");
+ }
+
+ [Test]
+ public void AvailableFrameworksList_ContainsNoDuplicates()
+ {
+ var names = new List();
+ foreach (var framework in _runtimeService.AvailableRuntimes)
+ names.Add(framework.DisplayName);
+ Assert.That(names, Is.Unique);
+ }
+
[TestCase("mono", 2, 0, "net-4.0")]
[TestCase("net", 2, 0, "net-4.0")]
[TestCase("net", 3, 5, "net-4.0")]
@@ -75,15 +128,15 @@ public void EngineOptionPreferredOverImageTarget(string framework, int majorVers
public void RuntimeFrameworkIsSetForSubpackages()
{
//Runtime Service verifies that requested frameworks are available, therefore this test can only currently be run on platforms with both CLR v2 and v4 available
- Assume.That(new RuntimeFramework(RuntimeType.Net, new Version("2.0.50727")), Has.Property(nameof(RuntimeFramework.IsAvailable)).True);
- Assume.That(new RuntimeFramework(RuntimeType.Net, new Version("4.0.30319")), Has.Property(nameof(RuntimeFramework.IsAvailable)).True);
+ Assume.That(_runtimeService.IsAvailable("net-2.0"));
+ Assume.That(_runtimeService.IsAvailable("net-4.0"));
var topLevelPackage = new TestPackage(new [] {"a.dll", "b.dll"});
var net20Package = topLevelPackage.SubPackages[0];
- net20Package.Settings.Add(InternalEnginePackageSettings.ImageRuntimeVersion, new Version("2.0.50727"));
+ net20Package.Settings.Add(InternalEnginePackageSettings.ImageRuntimeVersion, new Version("2.0"));
var net40Package = topLevelPackage.SubPackages[1];
- net40Package.Settings.Add(InternalEnginePackageSettings.ImageRuntimeVersion, new Version("4.0.30319"));
+ net40Package.Settings.Add(InternalEnginePackageSettings.ImageRuntimeVersion, new Version("4.0"));
_runtimeService.SelectRuntimeFramework(topLevelPackage);
diff --git a/src/NUnitEngine/nunit.engine/Runtime.cs b/src/NUnitEngine/nunit.engine/Runtime.cs
new file mode 100644
index 000000000..899c37559
--- /dev/null
+++ b/src/NUnitEngine/nunit.engine/Runtime.cs
@@ -0,0 +1,160 @@
+// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
+
+using System;
+
+namespace NUnit.Engine
+{
+ ///
+ /// Runtime class represents a specific Runtime, which may be
+ /// available in one or more versions. To define new Runtimes,
+ /// add a new member to the RuntimeType enum and then update
+ /// the SetProperties method in this class.
+ ///
+ public abstract class Runtime
+ {
+ #region Static Properties and Methods
+
+ // NOTE: The following are the only instances, which should
+ // ever exist, since the nested classes are private.
+
+ /// Microsoft .NET Framework
+ public static Runtime Net { get; } = new NetFrameworkRuntime();
+
+ /// Mono
+ public static Runtime Mono { get; } = new MonoRuntime();
+
+ /// NetCore
+ public static Runtime NetCore { get; } = new NetCoreRuntime();
+
+ public static Runtime Parse(string s)
+ {
+ switch (s.ToLower())
+ {
+ case "net":
+ return Runtime.Net;
+ case "mono":
+ return Runtime.Mono;
+ case "netcore":
+ return Runtime.NetCore;
+ default:
+ throw new NUnitEngineException($"Invalid runtime specified: {s}");
+ }
+ }
+
+ public static Runtime FromFrameworkIdentifier(string s)
+ {
+ switch (s)
+ {
+ case FrameworkIdentifiers.NetFramework:
+ return Runtime.Net;
+ case FrameworkIdentifiers.NetCoreApp:
+ return Runtime.NetCore;
+ case FrameworkIdentifiers.NetStandard:
+ throw new NUnitEngineException(
+ "Test assemblies must target a specific platform, rather than .NETStandard.");
+ }
+
+ throw new NUnitEngineException("Unrecognized Target Framework Identifier: " + s);
+ }
+
+ #endregion
+
+ #region Absract Properties and Methods
+
+ public abstract string DisplayName { get; }
+
+ public abstract string FrameworkIdentifier { get; }
+
+ public abstract bool Matches(Runtime targetRuntime);
+
+ public abstract Version GetClrVersionForFramework(Version frameworkVersion);
+
+ #endregion
+
+ #region Nested Runtime Classes
+
+ private class NetFrameworkRuntime : Runtime
+ {
+ public override string DisplayName => ".NET";
+ public override string FrameworkIdentifier => FrameworkIdentifiers.NetFramework;
+
+ public override string ToString() => "Net";
+ public override bool Matches(Runtime targetRuntime) => targetRuntime is NetFrameworkRuntime;
+
+ public override Version GetClrVersionForFramework(Version frameworkVersion)
+ {
+ switch (frameworkVersion.Major)
+ {
+ case 1:
+ switch (frameworkVersion.Minor)
+ {
+ case 0:
+ return new Version(1, 0, 3705);
+ case 1:
+ return new Version(1, 1, 4322);
+ }
+ break;
+ case 2:
+ case 3:
+ return new Version(2, 0, 50727);
+ case 4:
+ return new Version(4, 0, 30319);
+ }
+
+ throw new ArgumentException($"Unknown version for .NET Framework: {frameworkVersion}", "version");
+ }
+ }
+
+ private class MonoRuntime : NetFrameworkRuntime
+ {
+ public override string DisplayName => "Mono";
+
+ public override string ToString() => "Mono";
+
+ public override Version GetClrVersionForFramework(Version frameworkVersion)
+ {
+ switch (frameworkVersion.Major)
+ {
+ case 1:
+ return new Version(1, 1, 4322);
+ case 2:
+ case 3:
+ return new Version(2, 0, 50727);
+ case 4:
+ return new Version(4, 0, 30319);
+ }
+
+ throw new ArgumentException($"Unknown version for Mono runtime: {frameworkVersion}", "version");
+ }
+ }
+
+ private class NetCoreRuntime : Runtime
+ {
+ public override string DisplayName => ".NETCore";
+ public override string FrameworkIdentifier => FrameworkIdentifiers.NetCoreApp;
+
+ public override string ToString() => "NetCore";
+ public override bool Matches(Runtime targetRuntime) => targetRuntime is NetCoreRuntime;
+
+ public override Version GetClrVersionForFramework(Version frameworkVersion)
+ {
+ switch(frameworkVersion.Major)
+ {
+ case 1:
+ case 2:
+ return new Version(4, 0, 30319);
+ case 3:
+ return new Version(3, 1, 10);
+ case 5:
+ return new Version(5, 0, 1);
+ case 6:
+ return new Version(6, 0, 0);
+ }
+
+ throw new ArgumentException($"Unknown .NET Core version: {frameworkVersion}", "version");
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/NUnitEngine/nunit.engine/RuntimeFramework.cs b/src/NUnitEngine/nunit.engine/RuntimeFramework.cs
new file mode 100644
index 000000000..040a5db82
--- /dev/null
+++ b/src/NUnitEngine/nunit.engine/RuntimeFramework.cs
@@ -0,0 +1,220 @@
+// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
+
+#if NETFRAMEWORK
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using Microsoft.Win32;
+using NUnit.Common;
+using NUnit.Engine.Compatibility;
+
+namespace NUnit.Engine
+{
+ ///
+ /// RuntimeFramework represents a particular version
+ /// of a common language runtime implementation.
+ ///
+ [Serializable]
+ public sealed class RuntimeFramework : IRuntimeFramework
+ {
+ #region Constructors
+
+ ///
+ /// Construct from a Runtime and Version.
+ ///
+ /// A Runtime instance
+ /// The Version of the framework
+ public RuntimeFramework(Runtime runtime, Version version)
+ : this(runtime, version, null)
+ {
+ }
+
+ ///
+ /// Construct from a Runtime, Version and profile.
+ ///
+ /// A Runtime instance.
+ /// The Version of the framework.
+ /// A string representing the profile of the framework. Null if unspecified.
+ public RuntimeFramework(Runtime runtime, Version version, string profile)
+ {
+ Guard.ArgumentNotNull(runtime, nameof(runtime));
+ Guard.ArgumentValid(IsValidFrameworkVersion(version), $"{version} is not a valid framework version", nameof(version));
+
+ Runtime = runtime;
+ FrameworkVersion = ClrVersion = version;
+ ClrVersion = runtime.GetClrVersionForFramework(version);
+
+ Profile = profile;
+
+ DisplayName = GetDefaultDisplayName(runtime, FrameworkVersion, profile);
+
+ FrameworkName = new FrameworkName(runtime.FrameworkIdentifier, FrameworkVersion);
+ }
+
+ private bool IsValidFrameworkVersion(Version v)
+ {
+ // All known framework versions have either two components or
+ // three. If three, then the Build is currently less than 3.
+ return v.Major > 0 && v.Minor >= 0 && v.Build < 3 && v.Revision == -1;
+ }
+
+ #endregion
+
+ #region IRuntimeFramework Implementation
+
+ ///
+ /// Gets the unique Id for this runtime, such as "net-4.5"
+ ///
+ public string Id => Runtime.ToString().ToLower() + "-" + FrameworkVersion.ToString();
+
+ public FrameworkName FrameworkName { get; }
+
+ ///
+ /// The type of this runtime framework
+ ///
+ public Runtime Runtime { get; }
+
+ ///
+ /// The framework version for this runtime framework
+ ///
+ public Version FrameworkVersion { get; private set; }
+
+ ///
+ /// The CLR version for this runtime framework
+ ///
+ public Version ClrVersion { get; set; }
+
+ ///
+ /// The Profile for this framework, where relevant.
+ /// May be null and will have different sets of
+ /// values for each Runtime.
+ ///
+ public string Profile { get; private set; }
+
+ ///
+ /// Returns the Display name for this framework
+ ///
+ public string DisplayName { get; set; }
+
+ #endregion
+
+ ///
+ /// Parses a string representing a RuntimeFramework.
+ /// The string may be just a RuntimeType name or just
+ /// a Version or a hyphenated RuntimeType-Version or
+ /// a Version prefixed by 'v'.
+ ///
+ ///
+ ///
+ public static RuntimeFramework Parse(string s)
+ {
+ Guard.ArgumentNotNullOrEmpty(s, nameof(s));
+
+ string[] parts = s.Split(new char[] { '-' });
+ Guard.ArgumentValid(parts.Length == 2 && parts[0].Length > 0 && parts[1].Length > 0, "RuntimeFramework id not in correct format", nameof(s));
+
+ var runtime = Runtime.Parse(parts[0]);
+ var version = new Version(parts[1]);
+ return new RuntimeFramework(runtime, version);
+ }
+
+ public static bool TryParse(string s, out RuntimeFramework runtimeFramework)
+ {
+ try
+ {
+ runtimeFramework = Parse(s);
+ return true;
+ }
+ catch
+ {
+ runtimeFramework = null;
+ return false;
+ }
+ }
+
+ public static RuntimeFramework FromFrameworkName(string frameworkName)
+ {
+ return FromFrameworkName(new FrameworkName(frameworkName));
+ }
+
+ public static RuntimeFramework FromFrameworkName(FrameworkName frameworkName)
+ {
+ return new RuntimeFramework(Runtime.FromFrameworkIdentifier(frameworkName.Identifier), frameworkName.Version, frameworkName.Profile);
+ }
+
+ ///
+ /// Overridden to return the short name of the framework
+ ///
+ ///
+ public override string ToString() => Id;
+
+ ///
+ /// Returns true if the current framework matches the
+ /// one supplied as an argument. Both the RuntimeType
+ /// and the version must match.
+ ///
+ /// Two RuntimeTypes match if they are equal, if either one
+ /// is RuntimeType.Any or if one is RuntimeType.Net and
+ /// the other is RuntimeType.Mono.
+ ///
+ /// Two versions match if all specified version components
+ /// are equal. Negative (i.e. unspecified) version
+ /// components are ignored.
+ ///
+ /// The RuntimeFramework to be matched.
+ /// true on match, otherwise false
+ public bool Supports(RuntimeFramework target)
+ {
+ if (!Runtime.Matches(target.Runtime))
+ return false;
+
+ return VersionsMatch(this.ClrVersion, target.ClrVersion)
+ && this.FrameworkVersion.Major >= target.FrameworkVersion.Major
+ && this.FrameworkVersion.Minor >= target.FrameworkVersion.Minor;
+ }
+
+ public bool CanLoad(IRuntimeFramework requested)
+ {
+ return FrameworkVersion >= requested.FrameworkVersion;
+ }
+
+ private static string GetDefaultDisplayName(Runtime runtime, Version version, string profile)
+ {
+ string displayName = $"{runtime.DisplayName} {version}";
+
+ if (!string.IsNullOrEmpty(profile) && profile != "Full")
+ displayName += " - " + profile;
+
+ return displayName;
+ }
+
+ private static bool VersionsMatch(Version v1, Version v2)
+ {
+ return v1.Major == v2.Major &&
+ v1.Minor == v2.Minor &&
+ (v1.Build < 0 || v2.Build < 0 || v1.Build == v2.Build) &&
+ (v1.Revision < 0 || v2.Revision < 0 || v1.Revision == v2.Revision);
+ }
+
+ private static string GetMonoPrefixFromAssembly(Assembly assembly)
+ {
+ string prefix = assembly.Location;
+
+ // In all normal mono installations, there will be sufficient
+ // levels to complete the four iterations. But just in case
+ // files have been copied to some non-standard place, we check.
+ for (int i = 0; i < 4; i++)
+ {
+ string dir = Path.GetDirectoryName(prefix);
+ if (string.IsNullOrEmpty(dir)) break;
+
+ prefix = dir;
+ }
+
+ return prefix;
+ }
+ }
+}
+#endif
diff --git a/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs b/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs
index dcfd0f568..91cbce7f8 100644
--- a/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs
+++ b/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs
@@ -27,7 +27,7 @@ public AgentProcess(TestAgency agency, TestPackage package, Guid agentId)
bool loadUserProfile = package.GetSetting(EnginePackageSettings.LoadUserProfile, false);
string workDirectory = package.GetSetting(EnginePackageSettings.WorkDirectory, string.Empty);
- string agencyUrl = TargetRuntime.Runtime == RuntimeType.NetCore ? agency.TcpEndPoint : agency.RemotingUrl;
+ string agencyUrl = TargetRuntime.Runtime == Runtime.NetCore ? agency.TcpEndPoint : agency.RemotingUrl;
AgentArgs = new StringBuilder($"{agentId} {agencyUrl} --pid={Process.GetCurrentProcess().Id}");
// Set options that need to be in effect before the package
@@ -48,20 +48,20 @@ public AgentProcess(TestAgency agency, TestPackage package, Guid agentId)
StartInfo.WorkingDirectory = Environment.CurrentDirectory;
EnableRaisingEvents = true;
- if (TargetRuntime.Runtime == RuntimeType.Mono)
+ if (TargetRuntime.Runtime == Runtime.Mono)
{
- StartInfo.FileName = RuntimeFramework.MonoExePath;
+ StartInfo.FileName = RuntimeFrameworkService.MonoExePath;
string monoOptions = "--runtime=v" + TargetRuntime.ClrVersion.ToString(3);
monoOptions += " --debug";
StartInfo.Arguments = string.Format("{0} \"{1}\" {2}", monoOptions, AgentExePath, AgentArgs);
}
- else if (TargetRuntime.Runtime == RuntimeType.Net)
+ else if (TargetRuntime.Runtime == Runtime.Net)
{
StartInfo.FileName = AgentExePath;
StartInfo.Arguments = AgentArgs.ToString();
StartInfo.LoadUserProfile = loadUserProfile;
}
- else if (TargetRuntime.Runtime == RuntimeType.NetCore)
+ else if (TargetRuntime.Runtime == Runtime.NetCore)
{
StartInfo.FileName = "dotnet";
StartInfo.Arguments = $"{AgentExePath} {AgentArgs}";
@@ -117,15 +117,14 @@ public static string GetTestAgentExePath(RuntimeFramework targetRuntime, bool re
string agentName;
string agentExtension;
int major = targetRuntime.FrameworkVersion.Major;
- switch (targetRuntime.Runtime)
+ switch (targetRuntime.Runtime.FrameworkIdentifier)
{
- case RuntimeType.Net:
- case RuntimeType.Mono:
+ case FrameworkIdentifiers.NetFramework:
runtimeDir = major >= 4 ? "net40" : "net20";
agentName = requires32Bit ? "nunit-agent-x86" : "nunit-agent";
agentExtension = ".exe";
break;
- case RuntimeType.NetCore:
+ case FrameworkIdentifiers.NetCoreApp:
runtimeDir = major >= 6 ? "net6.0" : major == 5 ? "net5.0" : "netcoreapp3.1";
agentName = "nunit-agent";
agentExtension = ".dll";
diff --git a/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs b/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs
index bd5cecf99..633b0566b 100644
--- a/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs
+++ b/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Reflection;
using Mono.Cecil;
using NUnit.Common;
using NUnit.Engine.Internal;
@@ -13,22 +14,34 @@
namespace NUnit.Engine.Services
{
+ using Microsoft.Win32;
+ using RuntimeLocators;
+
public class RuntimeFrameworkService : Service, IRuntimeFrameworkService, IAvailableRuntimes
{
static readonly Logger log = InternalTrace.GetLogger(typeof(RuntimeFrameworkService));
- // HACK: This line forces RuntimeFramework to initialize the static property
- // AvailableFrameworks before it is accessed by multiple threads. See comment
- // on RuntimeFramework class for a more detailled explanation.
- static readonly RuntimeFramework[] _availableRuntimes = RuntimeFramework.AvailableFrameworks;
+ private List _availableRuntimes = new List();
+
+ ///
+ /// Gets a RuntimeFramework instance representing the runtime under
+ /// which the code is currently running.
+ ///
+ public IRuntimeFramework CurrentFramework { get; private set; }
+
+ private static string MonoPrefix;
+
+ ///
+ /// The path to the mono executable, if we are running on Mono.
+ ///
+ public static string MonoExePath => MonoPrefix != null && Environment.OSVersion.Platform == PlatformID.Win32NT
+ ? Path.Combine(MonoPrefix, "bin/mono.exe")
+ : "mono";
///
/// Gets a list of available runtimes.
///
- public IList AvailableRuntimes
- {
- get { return _availableRuntimes; }
- }
+ public IList AvailableRuntimes => _availableRuntimes.ToArray();
///
/// Returns true if the runtime framework represented by
@@ -43,7 +56,7 @@ public bool IsAvailable(string name)
if (!RuntimeFramework.TryParse(name, out RuntimeFramework requestedFramework))
throw new NUnitEngineException("Invalid or unknown framework requested: " + name);
- foreach (var framework in RuntimeFramework.AvailableFrameworks)
+ foreach (var framework in _availableRuntimes)
if (FrameworksMatch(requestedFramework, framework))
return true;
@@ -69,15 +82,15 @@ private static bool FrameworksMatch(RuntimeFramework requested, RuntimeFramework
(requestedVersion.Revision < 0 || availableVersion.Revision < 0 || requestedVersion.Revision == availableVersion.Revision);
}
- private static bool RuntimesMatch(RuntimeType requested, RuntimeType available)
+ private static bool RuntimesMatch(Runtime requested, Runtime available)
{
- if (requested == available || requested == RuntimeType.Any)
+ if (requested == available)
return true;
- if (requested == RuntimeType.Net && available == RuntimeType.Mono)
+ if (requested == Runtime.Net && available == Runtime.Mono)
return true;
- if (requested == RuntimeType.Mono && available == RuntimeType.Net)
+ if (requested == Runtime.Mono && available == Runtime.Net)
return true;
return false;
@@ -108,7 +121,7 @@ private RuntimeFramework SelectRuntimeFrameworkInner(TestPackage package)
}
// Examine the provided settings
- RuntimeFramework currentFramework = RuntimeFramework.CurrentFramework;
+ IRuntimeFramework currentFramework = CurrentFramework;
log.Debug("Current framework is " + currentFramework);
string frameworkSetting = package.GetSetting(EnginePackageSettings.RequestedRuntimeFramework, "");
@@ -131,14 +144,15 @@ private RuntimeFramework SelectRuntimeFrameworkInner(TestPackage package)
log.Debug($"No specific framework requested for {package.Name}");
string imageTargetFrameworkNameSetting = package.GetSetting(InternalEnginePackageSettings.ImageTargetFrameworkName, "");
- RuntimeType targetRuntime;
+ Runtime targetRuntime;
Version targetVersion;
if (string.IsNullOrEmpty(imageTargetFrameworkNameSetting))
{
// Assume .NET Framework
- targetRuntime = currentFramework.Runtime;
- targetVersion = package.GetSetting(InternalEnginePackageSettings.ImageRuntimeVersion, new Version(2, 0));
+ targetRuntime = Runtime.Net;
+ var trialVersion = package.GetSetting(InternalEnginePackageSettings.ImageRuntimeVersion, new Version(2, 0));
+ targetVersion = new Version(trialVersion.Major, trialVersion.Minor);
}
else
{
@@ -147,10 +161,10 @@ private RuntimeFramework SelectRuntimeFrameworkInner(TestPackage package)
switch (frameworkName.Identifier)
{
case ".NETFramework":
- targetRuntime = RuntimeType.Net;
+ targetRuntime = Runtime.Net;
break;
case ".NETCoreApp":
- targetRuntime = RuntimeType.NetCore;
+ targetRuntime = Runtime.NetCore;
break;
default:
throw new NUnitEngineException("Unsupported Target Framework: " + imageTargetFrameworkNameSetting);
@@ -159,7 +173,7 @@ private RuntimeFramework SelectRuntimeFrameworkInner(TestPackage package)
targetVersion = frameworkName.Version;
}
- if (!new RuntimeFramework(targetRuntime, targetVersion).IsAvailable)
+ if (!IsAvailable(new RuntimeFramework(targetRuntime, targetVersion).Id))
{
log.Debug("Preferred version {0} is not installed or this NUnit installation does not support it", targetVersion);
if (targetVersion < currentFramework.FrameworkVersion)
@@ -173,6 +187,21 @@ private RuntimeFramework SelectRuntimeFrameworkInner(TestPackage package)
return targetFramework;
}
+ public override void StartService()
+ {
+ try
+ {
+ SetCurrentFramework();
+ FindAvailableRuntimes();
+ }
+ catch
+ {
+ Status = ServiceStatus.Error;
+ throw;
+ }
+
+ Status = ServiceStatus.Started;
+ }
///
/// Returns the best available framework that matches a target framework.
@@ -194,6 +223,118 @@ public RuntimeFramework GetBestAvailableFramework(RuntimeFramework target)
return result;
}
+ private void SetCurrentFramework()
+ {
+ Type monoRuntimeType = Type.GetType("Mono.Runtime", false);
+ bool isMono = monoRuntimeType != null;
+
+ Runtime runtime = isMono
+ ? Runtime.Mono
+ : Runtime.Net;
+
+ int major = Environment.Version.Major;
+ int minor = Environment.Version.Minor;
+
+ if (isMono)
+ {
+ switch (major)
+ {
+ case 1:
+ minor = 0;
+ break;
+ case 2:
+ major = 3;
+ minor = 5;
+ break;
+ }
+ }
+ else /* It's windows */
+ if (major == 2)
+ {
+ RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework");
+ if (key != null)
+ {
+ string installRoot = key.GetValue("InstallRoot") as string;
+ if (installRoot != null)
+ {
+ if (Directory.Exists(Path.Combine(installRoot, "v3.5")))
+ {
+ major = 3;
+ minor = 5;
+ }
+ else if (Directory.Exists(Path.Combine(installRoot, "v3.0")))
+ {
+ major = 3;
+ minor = 0;
+ }
+ }
+ }
+ }
+ else if (major == 4 && Type.GetType("System.Reflection.AssemblyMetadataAttribute") != null)
+ {
+ minor = 5;
+ }
+
+ var currentFramework = new RuntimeFramework(runtime, new Version(major, minor))
+ {
+ ClrVersion = Environment.Version
+ };
+
+ if (isMono)
+ {
+ MonoPrefix = GetMonoPrefixFromAssembly(monoRuntimeType.Assembly);
+
+ MethodInfo getDisplayNameMethod = monoRuntimeType.GetMethod(
+ "GetDisplayName", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.ExactBinding);
+ if (getDisplayNameMethod != null)
+ {
+ string displayName = (string)getDisplayNameMethod.Invoke(null, new object[0]);
+
+ int space = displayName.IndexOf(' ');
+ if (space >= 3) // Minimum length of a version
+ {
+ string version = displayName.Substring(0, space);
+ displayName = "Mono " + version;
+ }
+ else
+ displayName = "Mono " + displayName;
+
+ currentFramework.DisplayName = displayName;
+ }
+ }
+
+ CurrentFramework = currentFramework;
+ }
+
+ private static string GetMonoPrefixFromAssembly(Assembly assembly)
+ {
+ string prefix = assembly.Location;
+
+ // In all normal mono installations, there will be sufficient
+ // levels to complete the four iterations. But just in case
+ // files have been copied to some non-standard place, we check.
+ for (int i = 0; i < 4; i++)
+ {
+ string dir = Path.GetDirectoryName(prefix);
+ if (string.IsNullOrEmpty(dir)) break;
+
+ prefix = dir;
+ }
+
+ return prefix;
+ }
+
+ private void FindAvailableRuntimes()
+ {
+ _availableRuntimes = new List();
+
+ if (Environment.OSVersion.Platform == PlatformID.Win32NT)
+ _availableRuntimes.AddRange(NetFxRuntimeLocator.FindRuntimes());
+
+ //FindDefaultMonoFramework();
+ _availableRuntimes.AddRange(NetCoreRuntimeLocator.FindRuntimes());
+ }
+
///
/// Use Mono.Cecil to get information about all assemblies and
/// apply it to the package using special internal keywords.
diff --git a/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/MonoRuntimeLocator.cs b/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/MonoRuntimeLocator.cs
new file mode 100644
index 000000000..da9a919d6
--- /dev/null
+++ b/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/MonoRuntimeLocator.cs
@@ -0,0 +1,137 @@
+// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
+
+#if NETFRAMEWORK && NYI
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+namespace NUnit.Engine.Services.RuntimeLocators
+{
+ public static class MonoRuntimeLocator
+ {
+ //public static IEnumerable FindRuntimes()
+ //{
+ // var current = RuntimeFramework.CurrentFramework;
+ // if (current.Runtime == Runtime.Mono)
+ // {
+ // yield return current;
+ // }
+ // else
+ // if (Environment.OSVersion.Platform == PlatformID.Win32NT)
+ // FindBestMonoFrameworkOnWindows();
+ //}
+
+ //private static void UseCurrentMonoFramework()
+ //{
+ // Debug.Assert(CurrentFramework.Runtime == Runtime.Mono && MonoPrefix != null && MonoVersion != null);
+
+ // // Multiple profiles are no longer supported with Mono 4.0
+ // if (RuntimeFramework.MonoVersion.Major < 4 && FindAllMonoProfiles() > 0)
+ // return;
+
+ // // If Mono 4.0+ or no profiles found, just use current runtime
+ // _availableFrameworks.Add(RuntimeFramework.CurrentFramework);
+ //}
+
+ //private static void FindBestMonoFrameworkOnWindows()
+ //{
+ // // First, look for recent frameworks that use the Software\Mono Key
+ // RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\Mono");
+
+ // if (key != null && (int)key.GetValue("Installed", 0) == 1)
+ // {
+ // string version = key.GetValue("Version") as string;
+ // MonoPrefix = key.GetValue("SdkInstallRoot") as string;
+
+ // if (version != null)
+ // {
+ // MonoVersion = new Version(version);
+ // AddMonoFramework(new Version(4, 5), null);
+ // return;
+ // }
+ // }
+
+ // // Some later 3.x Mono releases didn't use the registry
+ // // so check in standard location for them.
+ // if (Directory.Exists(DEFAULT_WINDOWS_MONO_DIR))
+ // {
+ // MonoPrefix = DEFAULT_WINDOWS_MONO_DIR;
+ // AddMonoFramework(new Version(4, 5), null);
+ // return;
+ // }
+
+ // // Look in the Software\Novell key for older versions
+ // key = Registry.LocalMachine.OpenSubKey(@"Software\Novell\Mono");
+ // if (key != null)
+ // {
+ // string version = key.GetValue("DefaultCLR") as string;
+ // if (version != null)
+ // {
+ // RegistryKey subKey = key.OpenSubKey(version);
+ // if (subKey != null)
+ // {
+ // MonoPrefix = subKey.GetValue("SdkInstallRoot") as string;
+ // MonoVersion = new Version(version);
+
+ // FindAllMonoProfiles();
+ // }
+ // }
+ // }
+ //}
+
+ //private static int FindAllMonoProfiles()
+ //{
+ // int count = 0;
+
+ // if (MonoPrefix != null)
+ // {
+ // if (File.Exists(Path.Combine(MonoPrefix, "lib/mono/1.0/mscorlib.dll")))
+ // {
+ // AddMonoFramework(new Version(1, 1, 4322), "1.0");
+ // count++;
+ // }
+
+ // if (File.Exists(Path.Combine(MonoPrefix, "lib/mono/2.0/mscorlib.dll")))
+ // {
+ // AddMonoFramework(new Version(2, 0), "2.0");
+ // count++;
+ // }
+
+ // if (Directory.Exists(Path.Combine(MonoPrefix, "lib/mono/3.5")))
+ // {
+ // AddMonoFramework(new Version(3, 5), "3.5");
+ // count++;
+ // }
+
+ // if (File.Exists(Path.Combine(MonoPrefix, "lib/mono/4.0/mscorlib.dll")))
+ // {
+ // AddMonoFramework(new Version(4, 0), "4.0");
+ // count++;
+ // }
+
+ // if (File.Exists(Path.Combine(MonoPrefix, "lib/mono/4.5/mscorlib.dll")))
+ // {
+ // AddMonoFramework(new Version(4, 5), "4.5");
+ // count++;
+ // }
+ // }
+
+ // return count;
+ //}
+
+ //private static void AddMonoFramework(Version frameworkVersion, string profile)
+ //{
+ // var framework = new RuntimeFramework(Runtime.Mono, frameworkVersion)
+ // {
+ // Profile = profile,
+ // DisplayName = MonoVersion != null
+ // ? "Mono " + MonoVersion.ToString() + " - " + profile + " Profile"
+ // : "Mono - " + profile + " Profile"
+ // };
+
+ // _availableFrameworks.Add(framework);
+ //}
+ }
+}
+#endif
diff --git a/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetCoreRuntimeLocator.cs b/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetCoreRuntimeLocator.cs
new file mode 100644
index 000000000..070d21b3a
--- /dev/null
+++ b/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetCoreRuntimeLocator.cs
@@ -0,0 +1,77 @@
+// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
+
+#if NETFRAMEWORK
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace NUnit.Engine.Services.RuntimeLocators
+{
+ public static class NetCoreRuntimeLocator
+ {
+ public static IEnumerable FindRuntimes()
+ {
+ const string WINDOWS_INSTALL_DIR = "C:\\Program Files\\dotnet\\";
+ const string LINUX_INSTALL_DIR = "/usr/shared/dotnet/";
+ string INSTALL_DIR = Path.DirectorySeparatorChar == '\\'
+ ? WINDOWS_INSTALL_DIR
+ : LINUX_INSTALL_DIR;
+ string runtimeDir = Path.Combine(INSTALL_DIR, Path.Combine("shared", "Microsoft.NETCore.App"));
+
+ if (Directory.Exists(INSTALL_DIR) &&
+ File.Exists(Path.Combine(INSTALL_DIR, "dotnet.exe")) &&
+ Directory.Exists(runtimeDir))
+ {
+ var dirList = new DirectoryInfo(runtimeDir).GetDirectories();
+ var dirNames = new List();
+ foreach (var dir in dirList)
+ dirNames.Add(dir.Name);
+
+ foreach (var runtime in GetNetCoreRuntimesFromDirectoryNames(dirNames))
+ yield return runtime;
+ }
+ }
+
+ // Deal with oddly named directories, which may sometimes appear when previews are installed
+ internal static IList GetNetCoreRuntimesFromDirectoryNames(IEnumerable dirNames)
+ {
+ const string VERSION_CHARS = ".0123456789";
+ var runtimes = new List();
+
+ foreach (string dirName in dirNames)
+ {
+ int len = 0;
+ foreach (char c in dirName)
+ {
+ if (VERSION_CHARS.IndexOf(c) >= 0)
+ len++;
+ else
+ break;
+ }
+
+ if (len == 0)
+ continue;
+
+ Version fullVersion = null;
+ try
+ {
+ fullVersion = new Version(dirName.Substring(0, len));
+ }
+ catch
+ {
+ continue;
+ }
+
+ var newVersion = new Version(fullVersion.Major, fullVersion.Minor);
+ int count = runtimes.Count;
+ if (count > 0 && runtimes[count - 1].FrameworkVersion == newVersion)
+ continue;
+
+ runtimes.Add(new RuntimeFramework(Runtime.NetCore, newVersion));
+ }
+
+ return runtimes;
+ }
+ }
+}
+#endif
diff --git a/src/NUnitEngine/nunit.engine.core/Internal/RuntimeFrameworks/DotNetFrameworkLocator.cs b/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetFxRuntimeLocator.cs
similarity index 90%
rename from src/NUnitEngine/nunit.engine.core/Internal/RuntimeFrameworks/DotNetFrameworkLocator.cs
rename to src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetFxRuntimeLocator.cs
index 92560e38a..1c7c6cd2a 100644
--- a/src/NUnitEngine/nunit.engine.core/Internal/RuntimeFrameworks/DotNetFrameworkLocator.cs
+++ b/src/NUnitEngine/nunit.engine/Services/RuntimeLocators/NetFxRuntimeLocator.cs
@@ -5,14 +5,14 @@
using System.Collections.Generic;
using Microsoft.Win32;
-namespace NUnit.Engine.Internal.RuntimeFrameworks
+namespace NUnit.Engine.Services.RuntimeLocators
{
- internal static class DotNetFrameworkLocator
+ public static class NetFxRuntimeLocator
{
// Note: this method cannot be generalized past V4, because (a) it has
// specific code for detecting .NET 4.5 and (b) we don't know what
// microsoft will do in the future
- public static IEnumerable FindDotNetFrameworks()
+ public static IEnumerable FindRuntimes()
{
// Handle Version 1.0, using a different registry key
foreach (var framework in FindExtremelyOldDotNetFrameworkVersions())
@@ -39,7 +39,7 @@ public static IEnumerable FindDotNetFrameworks()
else if (CheckInstallDword(versionKey))
{
// Versions 1.1, 2.0, 3.0 and 3.5 are possible here
- yield return new RuntimeFramework(RuntimeType.Net, new Version(name.Substring(1, 3)));
+ yield return new RuntimeFramework(Runtime.Net, new Version(name.Substring(1, 3)));
}
}
}
@@ -53,7 +53,7 @@ private static IEnumerable FindExtremelyOldDotNetFrameworkVers
yield break;
foreach (var build in key.GetValueNames())
- yield return new RuntimeFramework(RuntimeType.Net, new Version("1.0." + build));
+ yield return new RuntimeFramework(Runtime.Net, new Version("1.0." + build));
}
private struct MinimumRelease
@@ -91,12 +91,12 @@ private static IEnumerable FindDotNetFourFrameworkVersions(Reg
if (CheckInstallDword(profileKey))
{
- yield return new RuntimeFramework(RuntimeType.Net, new Version(4, 0), profile);
+ yield return new RuntimeFramework(Runtime.Net, new Version(4, 0), profile);
var release = (int)profileKey.GetValue("Release", 0);
foreach (var entry in ReleaseTable)
if (release >= entry.Release)
- yield return new RuntimeFramework(RuntimeType.Net, entry.Version);
+ yield return new RuntimeFramework(Runtime.Net, entry.Version);
yield break; //If full profile found don't check for client profile
diff --git a/src/NUnitEngine/nunit.engine/Services/TestAgency.cs b/src/NUnitEngine/nunit.engine/Services/TestAgency.cs
index 4f0591c19..42daca7c6 100644
--- a/src/NUnitEngine/nunit.engine/Services/TestAgency.cs
+++ b/src/NUnitEngine/nunit.engine/Services/TestAgency.cs
@@ -29,6 +29,7 @@ public partial class TestAgency : ITestAgency, IService
private readonly AgentStore _agentStore = new AgentStore();
private IRuntimeFrameworkService _runtimeService;
+ private IAvailableRuntimes _availableRuntimeService;
// Transports used for various target runtimes
private TestAgencyRemotingTransport _remotingTransport; // .NET Framework
@@ -62,7 +63,7 @@ public ITestAgent GetAgent(TestPackage package)
if (!_runtimeService.IsAvailable(targetRuntime.Id))
{
string msg = $"The {targetRuntime} framework is not available.\r\nAvailable frameworks:";
- foreach (var runtime in RuntimeFramework.AvailableFrameworks)
+ foreach (var runtime in _availableRuntimeService.AvailableRuntimes)
msg += $" {runtime}";
throw new ArgumentException(msg);
}
@@ -179,14 +180,19 @@ public void StopService()
public void StartService()
{
_runtimeService = ServiceContext.GetService();
- if (_runtimeService == null)
+ _availableRuntimeService = ServiceContext.GetService();
+
+ if (_runtimeService == null || _availableRuntimeService == null)
+ {
Status = ServiceStatus.Error;
- else
- try
- {
+ return;
+ }
+
+ try
+ {
_remotingTransport.Start();
- _tcpTransport.Start();
- Status = ServiceStatus.Started;
+ _tcpTransport.Start();
+ Status = ServiceStatus.Started;
}
catch
{