From 422350872f032311431fde6582ffc0ff4ecdb203 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Mon, 18 May 2026 07:19:14 -0700 Subject: [PATCH 1/2] Update recipe to version 2.0.0-beta.1 --- build.cake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.cake b/build.cake index ed5a751bb..6250c2886 100644 --- a/build.cake +++ b/build.cake @@ -1,7 +1,6 @@ // Load the recipe -#load nuget:?package=NUnit.Cake.Recipe&version=2.0.0-dev100 +#load nuget:?package=NUnit.Cake.Recipe&version=2.0.0-beta.1 // Comment out above line and uncomment below for local tests of recipe changes -//#load ../NUnit.Cake.Recipe/recipe/*.cs //#load ../NUnit.Cake.Recipe/recipe/*.cake // Load additional cake files From 6b351532651d1e6c76b8bc13cf5c628643235ef5 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Tue, 19 May 2026 10:10:44 -0700 Subject: [PATCH 2/2] Support for V2 ResultWriter --- KnownExtensions.cake | 4 +- package-tests.cake | 59 +++++++++---------- .../nunit4-console/ConsoleRunner.cs | 12 ++-- .../nunit.engine/Services/ResultService.cs | 47 +++++++++------ 4 files changed, 66 insertions(+), 56 deletions(-) diff --git a/KnownExtensions.cake b/KnownExtensions.cake index 688fc3b46..4fa83e7fe 100644 --- a/KnownExtensions.cake +++ b/KnownExtensions.cake @@ -11,7 +11,9 @@ public static class KnownExtensions public static ExtensionSpecifier VSProjectLoader = new ExtensionSpecifier( "NUnit.Extension.VSProjectLoader", "nunit-extension-vs-project-loader", "3.9.0"); public static ExtensionSpecifier NUnitV2ResultWriter = new ExtensionSpecifier( - "NUnit.Extension.NUnitV2ResultWriter", "nunit-extension-nunit-v2-result-writer", "4.0.0-beta.1"); + "NUnit.Extension.NUnitV2ResultWriter", "nunit-extension-nunit-v2-result-writer", "4.0.0-dev.14"); + public static ExtensionSpecifier TeamCityEventListener = new ExtensionSpecifier( + "NUnit.Extension.TeamCityEventListener", "nunit-extension-teamcity-event-listener", "1.0.9"); public static ExtensionSpecifier Net462PluggableAgent = new ExtensionSpecifier( "NUnit.Extension.Net462PluggableAgent", "nunit-extension-net462-pluggable-agent", "4.1.0-alpha.5"); public static ExtensionSpecifier Net80PluggableAgent = new ExtensionSpecifier( diff --git a/package-tests.cake b/package-tests.cake index 81730820c..dc6268728 100644 --- a/package-tests.cake +++ b/package-tests.cake @@ -11,21 +11,6 @@ void AddToBothLists(PackageTest test) NetCoreRunnerTests.Add(test); } -public static class Extensions -{ - // Extensions used in tests, with version specified. - public static ExtensionSpecifier NUnitV2Driver = new ExtensionSpecifier( - "NUnit.Extension.NUnitV2Driver", "nunit-extension-nunit-v2-driver", "3.9.0"); - public static ExtensionSpecifier NUnitProjectLoader = new ExtensionSpecifier( - "NUnit.Extension.NUnitProjectLoader", "nunit-extension-nunit-project-loader", "3.8.0"); - public static ExtensionSpecifier VSProjectLoader = new ExtensionSpecifier( - "NUnit.Extension.VSProjectLoader", "nunit-extension-vs-project-loader", "3.9.0"); - public static ExtensionSpecifier NUnitV2ResultWriter = new ExtensionSpecifier( - "NUnit.Extension.NUnitV2ResultWriter", "nunit-extension-nunit-v2-result-writer", "3.8.0"); - public static ExtensionSpecifier TeamCityEventListener = new ExtensionSpecifier( - "NUnit.Extension.TeamCityEventListener", "nunit-extension-teamcity-event-listener", "1.0.9"); -} - ////////////////////////////////////////////////////////////////////// // RUN MOCK-ASSEMBLY UNDER EACH RUNTIME ////////////////////////////////////////////////////////////////////// @@ -423,10 +408,18 @@ StandardRunnerTests.Add(new PackageTest(1, "ExtensionsInstalledFromAddedDirector NetCoreRunnerTests.Add(new PackageTest(1, "ExtensionsInstalledFromAddedDirectory") { Description = "List Extensions shows extension from added directory", - Arguments = "--extensionDirectory fakes/netstandard2.0 --list-extensions --trace:Debug", + Arguments = "--extensionDirectory fakes/netstandard2.0 --list-extensions", ExpectedOutput = new[] { Contains("Extension:", exactly: 5) } }); +AddToBothLists(new PackageTest(1, "SpecificExtensionInstalled") +{ + Description = "List Extensions shows V2ResultWriter if we include it as needed", + Arguments = "--list-extensions", + ExpectedOutput = new[] { Contains("nunit-v2-result-writer.dll", exactly: 1) }, + ExtensionsNeeded = new[] { KnownExtensions.NUnitV2ResultWriter } +}); + ////////////////////////////////////////////////////////////////////// // TEST OF ASSEMBLY RESOLUTION STATISTICS ////////////////////////////////////////////////////////////////////// @@ -518,22 +511,24 @@ NetCoreRunnerTests.Add(new PackageTest(1, "ListResolutionStatistics_Run") // ExtensionsNeeded = new[] { Extensions.NUnitProjectLoader } //}); -//// V2 Result Writer Test -//StandardRunnerTests.Add(new PackageTest(1, "V2ResultWriterTest_Net462") -//{ -// Description = "Run mock-assembly under .NET 4.6.2 and produce V2 output", -// Arguments = "testdata/net462/mock-assembly.dll --result=TestResult.xml --result=NUnit2TestResult.xml;format=nunit2", -// ExpectedResult = new MockAssemblyExpectedResult("net-4.6.2"), -// ExtensionsNeeded = new[] { Extensions.NUnitV2ResultWriter } -//}); +// V2 Result Writer Tests +StandardRunnerTests.Add(new PackageTest(1, "V2ResultWriterTest_Net462") +{ + Description = "Run mock-assembly under .NET 4.6.2 and produce V2 output", + Arguments = "testdata/net462/mock-assembly.dll --result=TestResult.xml --result=NUnit2TestResult.xml;format=nunit2", + ExpectedResult = new MockAssemblyExpectedResult("net-4.6.2"), + // TODO: Check that V2 result file was created + ExtensionsNeeded = new[] { KnownExtensions.NUnitV2ResultWriter } +}); -//StandardRunnerTests.Add(new PackageTest(1, "V2ResultWriterTest_Net60") -//{ -// Description = "Run mock-assembly under .NET 6.0 and produce V2 output", -// Arguments = "testdata/net6.0/mock-assembly.dll --result=TestResult.xml --result=NUnit2TestResult.xml;format=nunit2", -// ExpectedResult = new MockAssemblyExpectedResult("netcore-6.0"), -// ExtensionsNeeded = new[] { Extensions.NUnitV2ResultWriter } -//}); +AddToBothLists(new PackageTest(1, "V2ResultWriterTest_Net60") +{ + Description = "Run mock-assembly under .NET 6.0 and produce V2 output", + Arguments = "testdata/net6.0/mock-assembly.dll --result=TestResult.xml --result=NUnit2TestResult.xml;format=nunit2", + ExpectedResult = new MockAssemblyExpectedResult("netcore-6.0"), + // TODO: Check that V2 result file was created + ExtensionsNeeded = new[] { KnownExtensions.NUnitV2ResultWriter } +}); //// VS Project Loader Tests //StandardRunnerTests.Add(new PackageTest(1, "VSProjectLoaderTest_Project") @@ -578,7 +573,7 @@ StandardRunnerTests.Add(new PackageTest(1, "TeamCityListenerTest") Description = "Run mock-assembly with --teamcity enabled", Arguments = "testdata/net462/mock-assembly.dll --enable NUnit.Engine.Listeners.TeamCityEventListener", ExpectedResult = new MockAssemblyExpectedResult("net-4.6.2"), - ExtensionsNeeded = new[] { Extensions.TeamCityEventListener } + ExtensionsNeeded = new[] { KnownExtensions.TeamCityEventListener } }); //// V2 Framework Driver Tests diff --git a/src/NUnitConsole/nunit4-console/ConsoleRunner.cs b/src/NUnitConsole/nunit4-console/ConsoleRunner.cs index 06afd28a3..81dc8eeca 100644 --- a/src/NUnitConsole/nunit4-console/ConsoleRunner.cs +++ b/src/NUnitConsole/nunit4-console/ConsoleRunner.cs @@ -68,12 +68,6 @@ public ConsoleRunner(ITestEngine engine, ConsoleOptions options, ExtendedTextWri Guard.ArgumentNotNull(_outWriter = writer); // NOTE: Accessing Services triggers the engine to initialize all services - _resultService = _engine.Services.GetService(); - Guard.OperationValid(_resultService is not null, "Internal Error: ResultService was not found"); - - _filterService = _engine.Services.GetService(); - Guard.OperationValid(_filterService is not null, "Internal Error: TestFilterService was not found"); - _extensionService = _engine.Services.GetService(); Guard.OperationValid(_extensionService is not null, "Internal Error: ExtensionService was not found"); @@ -87,6 +81,12 @@ public ConsoleRunner(ITestEngine engine, ConsoleOptions options, ExtendedTextWri _extensionService.InstallExtensions(); + _resultService = _engine.Services.GetService(); + Guard.OperationValid(_resultService is not null, "Internal Error: ResultService was not found"); + + _filterService = _engine.Services.GetService(); + Guard.OperationValid(_filterService is not null, "Internal Error: TestFilterService was not found"); + _workDirectory = options.WorkDirectory ?? Directory.GetCurrentDirectory(); if (!Directory.Exists(_workDirectory)) diff --git a/src/NUnitEngine/nunit.engine/Services/ResultService.cs b/src/NUnitEngine/nunit.engine/Services/ResultService.cs index 7ad8c7d87..9c4ed3dbc 100644 --- a/src/NUnitEngine/nunit.engine/Services/ResultService.cs +++ b/src/NUnitEngine/nunit.engine/Services/ResultService.cs @@ -13,8 +13,8 @@ public class ResultService : Service, IResultService private static readonly Logger log = InternalTrace.GetLogger(typeof(ResultService)); private readonly string[] BUILT_IN_FORMATS = new string[] { "nunit3", "cases", "user" }; - private List _extensionNodes = new List(); + private ExtensionService? _extensionService; private string[]? _formats; [MemberNotNull(nameof(_formats))] @@ -24,12 +24,13 @@ public string[] Formats { if (_formats is null) { + Guard.OperationValid(_extensionService is not null, "Formats property may not be accessed before ResultService is started"); + var formatList = new List(BUILT_IN_FORMATS); - if (_extensionNodes is not null) - foreach (var node in _extensionNodes) - foreach (var format in node.GetValues("Format")) - formatList.Add(format); + foreach (var node in WriterNodes) + foreach (var format in node.GetValues("Format")) + formatList.Add(format); _formats = formatList.ToArray(); } @@ -61,14 +62,13 @@ public IResultWriter GetResultWriter(string format, params object?[]? args) return new XmlTransformResultWriter(args!); default: - if (_extensionNodes is not null) - foreach (var node in _extensionNodes) - foreach (var supported in node.GetValues("Format")) - if (supported == format && node.ExtensionObject is not null) - { - log.Debug($" Returning {node.TypeName}"); - return (IResultWriter)node.ExtensionObject; - } + foreach (var node in WriterNodes) + foreach (var supported in node.GetValues("Format")) + if (supported == format && node.ExtensionObject is not null) + { + log.Debug($" Returning {node.TypeName}"); + return (IResultWriter)node.ExtensionObject; + } throw new NUnitEngineException("ResultWriter not found for format: " + format); } @@ -80,10 +80,7 @@ public override void StartService() try { - var extensionService = ServiceContext.GetService(); - - if (extensionService is not null && extensionService.Status == ServiceStatus.Started) - _extensionNodes.AddRange(extensionService.GetExtensionNodes()); + _extensionService = ServiceContext.GetService(); // If there is no extension service, we start anyway using built-in writers Status = ServiceStatus.Started; @@ -94,5 +91,21 @@ public override void StartService() throw; } } + + private List? _writerNodes; + private List WriterNodes + { + get + { + if (_writerNodes is null) + { + Guard.OperationValid(_extensionService is not null, "WriterNodes property may not be accessed before ResultService is started"); + + _writerNodes = [.. _extensionService.GetExtensionNodes()]; + } + + return _writerNodes; + } + } } }