Skip to content

Commit 471c8dd

Browse files
committed
Add new interfaces and enable use of launchers internally
1 parent 2a4218d commit 471c8dd

File tree

18 files changed

+781
-234
lines changed

18 files changed

+781
-234
lines changed

package-tests.cake

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ StandardRunnerTests.Add(new PackageTest(1, "Net462Test")
5757
AddToBothLists(new PackageTest(1, "Net80Test")
5858
{
5959
Description = "Run mock-assembly.dll under .NET 8.0",
60-
Arguments = "testdata/net8.0/mock-assembly.dll",
60+
Arguments = "testdata/net8.0/mock-assembly.dll --trace:Debug",
6161
ExpectedResult = new MockAssemblyExpectedResult("netcore-8.0")
6262
});
6363

src/NUnitCommon/nunit.extensibility.tests/ExtensionManagerTests.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class ExtensionManagerTests
1818
#pragma warning disable 414
1919
private static readonly string[] KnownExtensionPointPaths =
2020
{
21+
"/NUnit/Engine/TypeExtensions/IAgentLauncher",
2122
"/NUnit/Engine/TypeExtensions/IDriverFactory",
2223
"/NUnit/Engine/TypeExtensions/IProjectLoader",
2324
"/NUnit/Engine/TypeExtensions/IResultWriter",
@@ -28,6 +29,7 @@ public class ExtensionManagerTests
2829

2930
private static readonly Type[] KnownExtensionPointTypes =
3031
{
32+
typeof(IAgentLauncher),
3133
typeof(IDriverFactory),
3234
typeof(IProjectLoader),
3335
typeof(IResultWriter),
@@ -36,8 +38,9 @@ public class ExtensionManagerTests
3638
typeof(IFrameworkDriver)
3739
};
3840

39-
private static readonly int[] KnownExtensionPointCounts = { 1, 1, 1, 2, 1, 0 };
41+
private static readonly int[] KnownExtensionPointCounts = { 1, 1, 1, 1, 2, 1, 0 };
4042

43+
private const string FAKE_AGENT_LAUNCHER_EXTENSION = "NUnit.Engine.Fakes.FakeAgentLauncherExtension";
4144
private const string FAKE_FRAMEWORK_DRIVER_EXTENSION = "NUnit.Engine.Fakes.FakeFrameworkDriverExtension";
4245
private const string FAKE_PROJECT_LOADER_EXTENSION = "NUnit.Engine.Fakes.FakeProjectLoaderExtension";
4346
private const string FAKE_RESULT_WRITER_EXTENSION = "NUnit.Engine.Fakes.FakeResultWriterExtension";
@@ -48,6 +51,7 @@ public class ExtensionManagerTests
4851

4952
private readonly string[] KnownExtensions =
5053
{
54+
FAKE_AGENT_LAUNCHER_EXTENSION,
5155
FAKE_FRAMEWORK_DRIVER_EXTENSION,
5256
FAKE_PROJECT_LOADER_EXTENSION,
5357
FAKE_RESULT_WRITER_EXTENSION,

src/NUnitEngine/agents/nunit-agent-net80/nunit-agent-net80.csproj

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
<ApplicationManifest>app.manifest</ApplicationManifest>
99
<ApplicationIcon>..\..\..\..\nunit.ico</ApplicationIcon>
1010
<GenerateSupportedRuntime>false</GenerateSupportedRuntime>
11-
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
1211
</PropertyGroup>
1312

1413
<PropertyGroup>

src/NUnitEngine/nunit.engine.api/EnginePackageSettings.cs

+10
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ public static class EnginePackageSettings
8888
/// </summary>
8989
public const string TargetRuntimeFramework = "TargetRuntimeFramework";
9090

91+
/// <summary>
92+
/// Indicates the name of the agent requested by the user.
93+
/// </summary>
94+
public const string RequestedAgentName = "RequestedAgentName";
95+
96+
/// <summary>
97+
/// Indicates the name of the agent that was actually used.
98+
/// </summary>
99+
public const string SelectedAgentName = "SelectedAgentName";
100+
91101
/// <summary>
92102
/// Bool flag indicating that the test should be run in a 32-bit process
93103
/// on a 64-bit system. By default, NUNit runs in a 64-bit process on
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
2+
3+
using System;
4+
using System.Diagnostics;
5+
using NUnit.Extensibility;
6+
7+
namespace NUnit.Engine.Extensibility
8+
{
9+
/// <summary>
10+
/// Interface implemented by an agent launcher, which is able to examine
11+
/// a test package, evaluate whether it can create an agent for it and
12+
/// create the agent itself on request.
13+
/// </summary>
14+
[TypeExtensionPoint(
15+
Description = "Launches an Agent Process for supported target runtimes")]
16+
public interface IAgentLauncher
17+
{
18+
/// <summary>
19+
/// Gets a TestAgentInfo describing this agent
20+
/// </summary>
21+
TestAgentInfo AgentInfo { get; }
22+
23+
/// <summary>
24+
/// Returns true if the launcher can create an agent for the supplied package, otherwise false.
25+
/// </summary>
26+
/// <param name="package"></param>
27+
/// <returns></returns>
28+
bool CanCreateAgent(TestPackage package);
29+
30+
/// <summary>
31+
/// Returns an agent capable of running the specified package.
32+
/// </summary>
33+
/// <param name="agentId"></param>
34+
/// <param name="agencyUrl"></param>
35+
/// <param name="package"></param>
36+
/// <returns></returns>
37+
Process CreateAgent(Guid agentId, string agencyUrl, TestPackage package);
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
2+
3+
using System.Collections.Generic;
4+
5+
namespace NUnit.Engine.Services
6+
{
7+
/// <summary>
8+
/// Objects implementing ITestAgentInfo by classes that can return information about
9+
/// available agents. The interface is used by runners in order to provide information
10+
/// to the user or to allow selecting agents.
11+
/// </summary>
12+
public interface ITestAgentInfo
13+
{
14+
/// <summary>
15+
/// Gets a list containing <see cref="TestAgentInfo"/> for all available agents.
16+
/// </summary>
17+
IList<TestAgentInfo> GetAvailableAgents();
18+
19+
/// <summary>
20+
/// Gets a list containing <see cref="TestAgentInfo"/> for any available agents,
21+
/// which are able to handle the specified package.
22+
/// </summary>
23+
/// <param name="package">A TestPackage</param>
24+
/// <returns>
25+
/// A list of suitable agents for running the package or an empty
26+
/// list if no agent is available for the package.
27+
/// </returns>
28+
IList<TestAgentInfo> GetAgentsForPackage(TestPackage package);
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
2+
3+
using System.Runtime.Versioning;
4+
5+
namespace NUnit.Engine
6+
{
7+
/// <summary>
8+
/// The TestAgentInfo struct provides information about an
9+
/// available agent for use by a runner.
10+
/// </summary>
11+
public struct TestAgentInfo
12+
{
13+
/// <summary>
14+
/// The name of this agent
15+
/// </summary>
16+
public string AgentName;
17+
18+
/// <summary>
19+
/// The agent type: InProcess, LocalProcess or RemoteProcess
20+
/// </summary>
21+
public TestAgentType AgentType;
22+
23+
/// <summary>
24+
/// The target runtime used by this agent
25+
/// </summary>
26+
public FrameworkName TargetRuntime;
27+
28+
/// <summary>
29+
/// Construct a TestAgent Info
30+
/// </summary>
31+
/// <param name="agentName">The agent name</param>
32+
/// <param name="agentType">The AgentType</param>
33+
/// <param name="targetRuntime">The target runtime</param>
34+
public TestAgentInfo(string agentName, TestAgentType agentType, FrameworkName targetRuntime)
35+
{
36+
AgentName = agentName;
37+
AgentType = agentType;
38+
TargetRuntime = targetRuntime;
39+
}
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
2+
3+
namespace NUnit.Engine
4+
{
5+
/// <summary>
6+
/// <para>TestAgentType is an enumeration of the types
7+
/// of agents, which may be available. Currently, three
8+
/// types are defined, of which one is implemented.</para>
9+
/// </summary>
10+
/// <remarks>
11+
/// A user requests a particular agent type by including a
12+
/// "TestAgentType" setting in the test package. The setting
13+
/// is optional and the runner will select an agent of any
14+
/// type available if it isn't specified.
15+
/// </remarks>
16+
public enum TestAgentType
17+
{
18+
/// <summary>
19+
/// Any agent type is acceptable. This is the default value,
20+
/// so it never needs to be specified by the user. This
21+
/// setting is not valid in the TestAgentInfo struct, since
22+
/// each agent must be of some particular type.
23+
/// </summary>
24+
Any = 0,
25+
26+
/// <summary>
27+
/// An in-process agent. This type is not directly supported
28+
/// by the engine but may be provided by an extension.
29+
/// </summary>
30+
InProcess = 1,
31+
32+
/// <summary>
33+
/// An agent running as a separate local process.
34+
/// A supplier for this type is built into the engine.
35+
/// </summary>
36+
LocalProcess = 2,
37+
38+
/// <summary>
39+
/// An agent running on a server, that is a separate
40+
/// machine, which may be specified in the request or
41+
/// left up to the agent supplier to determine. A supplier
42+
/// for this type may be developed in the future.
43+
/// </summary>
44+
RemoteProcess = 3
45+
}
46+
}

src/NUnitEngine/nunit.engine.tests/Services/AgentStoreTests.DummyTestAgent.cs

-34
This file was deleted.

src/NUnitEngine/nunit.engine.tests/Services/AgentStoreTests.cs

+38-6
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static void IdCannotBeReused()
3232
database.Register(DummyAgent);
3333
Assert.That(() => database.AddAgent(DummyAgentId, DummyProcess), Throws.ArgumentException.With.Property("ParamName").EqualTo("agentId"));
3434

35-
database.MarkTerminated(DummyAgentId);
35+
database.MarkProcessTerminated(DummyProcess);
3636
Assert.That(() => database.AddAgent(DummyAgentId, DummyProcess), Throws.ArgumentException.With.Property("ParamName").EqualTo("agentId"));
3737
}
3838

@@ -60,7 +60,7 @@ public static void AgentMustNotRegisterAfterTerminating()
6060
var database = new AgentStore();
6161

6262
database.AddAgent(DummyAgentId, DummyProcess);
63-
database.MarkTerminated(DummyAgentId);
63+
database.MarkProcessTerminated(DummyProcess);
6464
Assert.That(() => database.Register(DummyAgent), Throws.ArgumentException.With.Property("ParamName").EqualTo("agent"));
6565
}
6666

@@ -69,7 +69,8 @@ public static void AgentMustBeStartedBeforeTerminating()
6969
{
7070
var database = new AgentStore();
7171

72-
Assert.That(() => database.MarkTerminated(DummyAgentId), Throws.ArgumentException.With.Property("ParamName").EqualTo("agentId"));
72+
Assert.That(() => database.MarkProcessTerminated(DummyProcess),
73+
Throws.Exception.TypeOf<NUnitEngineException>().With.Message.EqualTo("Process terminated without registering an agent."));
7374
}
7475

7576
[Test]
@@ -107,7 +108,7 @@ public static void AgentIsNotReadyWhenTerminated()
107108

108109
database.AddAgent(DummyAgentId, DummyProcess);
109110
database.Register(DummyAgent);
110-
database.MarkTerminated(DummyAgentId);
111+
database.MarkProcessTerminated(DummyProcess);
111112
Assert.That(database.IsReady(DummyAgentId, out _), Is.False);
112113
}
113114

@@ -147,7 +148,7 @@ public static void AgentIsNotRunningWhenTerminated()
147148

148149
database.AddAgent(DummyAgentId, DummyProcess);
149150
database.Register(DummyAgent);
150-
database.MarkTerminated(DummyAgentId);
151+
database.MarkProcessTerminated(DummyProcess);
151152
Assert.That(database.IsAgentProcessActive(DummyAgentId, out _), Is.False);
152153
}
153154

@@ -169,11 +170,13 @@ public static void ConcurrentOperationsDoNotCorruptState()
169170
Assert.That(database.IsAgentProcessActive(id, out _), Is.True);
170171
Assert.That(database.IsReady(id, out _), Is.False);
171172

173+
// Pretend that the agent process started and registered
172174
database.Register(new DummyTestAgent(id));
173175
Assert.That(database.IsAgentProcessActive(id, out _), Is.True);
174176
Assert.That(database.IsReady(id, out _), Is.True);
175177

176-
database.MarkTerminated(id);
178+
//database.MarkProcessTerminated(DummyProcess);
179+
database.MarkAgentTerminated(id);
177180
Assert.That(database.IsAgentProcessActive(id, out _), Is.False);
178181
Assert.That(database.IsReady(id, out _), Is.False);
179182
}
@@ -210,6 +213,35 @@ private static void RunActionConcurrently(Action action, int threadCount)
210213
if (exceptions.Count != 0)
211214
throw exceptions[0];
212215
}
216+
217+
#region Nested DummyTestAgent Class
218+
219+
private sealed class DummyTestAgent : ITestAgent
220+
{
221+
public DummyTestAgent(Guid id)
222+
{
223+
Id = id;
224+
}
225+
226+
public Guid Id { get; }
227+
228+
public ITestEngineRunner CreateRunner(TestPackage package)
229+
{
230+
throw new NotImplementedException();
231+
}
232+
233+
public bool Start()
234+
{
235+
throw new NotImplementedException();
236+
}
237+
238+
public void Stop()
239+
{
240+
throw new NotImplementedException();
241+
}
242+
}
243+
244+
#endregion
213245
}
214246
}
215247
#endif

src/NUnitEngine/nunit.engine/Runners/ProcessRunner.cs

-3
Original file line numberDiff line numberDiff line change
@@ -261,9 +261,6 @@ private void CreateAgentAndRunnerIfNeeded()
261261
TestPackage.GetSetting(EnginePackageSettings.PauseBeforeRun, false);
262262

263263
_agent = _agency.GetAgent(TestPackage);
264-
265-
if (_agent is null)
266-
throw new NUnitEngineException("Unable to acquire remote process agent");
267264
}
268265

269266
if (_remoteRunner is null)

0 commit comments

Comments
 (0)