From b557ab1a704daf0058672ccc942aef61002367d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 15 Jun 2026 14:53:34 +0200 Subject: [PATCH 1/2] Extract shared report-provider registration helper (#9131) Factor the repeated CompositeExtensionFactory construction, TestApplicationBuilder guard, and data-consumer/session-lifetime/command-line registration shared by the CTRF, JUnit, and HTML report extensions into a new ReportProviderRegistration.AddReportProvider helper in SharedExtensionHelpers. Each Add*ReportProvider method is now a single expression-bodied call supplying only the generator factory and resource strings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../CtrfReportExtensions.cs | 22 +++------- ...osoft.Testing.Extensions.CtrfReport.csproj | 1 + .../HtmlReportExtensions.cs | 22 +++------- ...osoft.Testing.Extensions.HtmlReport.csproj | 1 + .../JUnitReportExtensions.cs | 22 +++------- ...soft.Testing.Extensions.JUnitReport.csproj | 1 + .../ReportProviderRegistration.cs | 43 +++++++++++++++++++ 7 files changed, 61 insertions(+), 51 deletions(-) create mode 100644 src/Platform/SharedExtensionHelpers/ReportProviderRegistration.cs diff --git a/src/Platform/Microsoft.Testing.Extensions.CtrfReport/CtrfReportExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.CtrfReport/CtrfReportExtensions.cs index 5d8a03502c..01485c02fa 100644 --- a/src/Platform/Microsoft.Testing.Extensions.CtrfReport/CtrfReportExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.CtrfReport/CtrfReportExtensions.cs @@ -4,7 +4,6 @@ using Microsoft.Testing.Extensions.CtrfReport; using Microsoft.Testing.Extensions.CtrfReport.Resources; using Microsoft.Testing.Platform.Builder; -using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Services; @@ -22,16 +21,11 @@ public static class CtrfReportExtensions /// /// The test application builder. public static void AddCtrfReportProvider(this ITestApplicationBuilder builder) - { - if (builder is not TestApplicationBuilder) - { - throw new InvalidOperationException(ExtensionResources.InvalidTestApplicationBuilderType); - } - - var commandLine = new CtrfReportGeneratorCommandLine(); - - var compositeCtrfReportGenerator = - new CompositeExtensionFactory(serviceProvider => + => ReportProviderRegistration.AddReportProvider( + builder, + ExtensionResources.InvalidTestApplicationBuilderType, + new CtrfReportGeneratorCommandLine(), + serviceProvider => new CtrfReportGenerator( serviceProvider.GetConfiguration(), serviceProvider.GetCommandLineOptions(), @@ -44,10 +38,4 @@ public static void AddCtrfReportProvider(this ITestApplicationBuilder builder) serviceProvider.GetTestFramework(), serviceProvider.GetTestApplicationProcessExitCode(), serviceProvider.GetLoggerFactory().CreateLogger())); - - builder.TestHost.AddDataConsumer(compositeCtrfReportGenerator); - builder.TestHost.AddTestSessionLifetimeHandler(compositeCtrfReportGenerator); - - builder.CommandLine.AddProvider(() => commandLine); - } } diff --git a/src/Platform/Microsoft.Testing.Extensions.CtrfReport/Microsoft.Testing.Extensions.CtrfReport.csproj b/src/Platform/Microsoft.Testing.Extensions.CtrfReport/Microsoft.Testing.Extensions.CtrfReport.csproj index 56dd893cce..d7a44e86c6 100644 --- a/src/Platform/Microsoft.Testing.Extensions.CtrfReport/Microsoft.Testing.Extensions.CtrfReport.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.CtrfReport/Microsoft.Testing.Extensions.CtrfReport.csproj @@ -56,6 +56,7 @@ This package extends Microsoft Testing Platform to produce test reports in the C + diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportExtensions.cs index eef9848ab2..41e5edf110 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportExtensions.cs @@ -4,7 +4,6 @@ using Microsoft.Testing.Extensions.HtmlReport; using Microsoft.Testing.Extensions.HtmlReport.Resources; using Microsoft.Testing.Platform.Builder; -using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Services; @@ -22,16 +21,11 @@ public static class HtmlReportExtensions /// /// The test application builder. public static void AddHtmlReportProvider(this ITestApplicationBuilder builder) - { - if (builder is not TestApplicationBuilder) - { - throw new InvalidOperationException(ExtensionResources.InvalidTestApplicationBuilderType); - } - - var commandLine = new HtmlReportGeneratorCommandLine(); - - var compositeHtmlReportGenerator = - new CompositeExtensionFactory(serviceProvider => + => ReportProviderRegistration.AddReportProvider( + builder, + ExtensionResources.InvalidTestApplicationBuilderType, + new HtmlReportGeneratorCommandLine(), + serviceProvider => new HtmlReportGenerator( serviceProvider.GetConfiguration(), serviceProvider.GetCommandLineOptions(), @@ -44,10 +38,4 @@ public static void AddHtmlReportProvider(this ITestApplicationBuilder builder) serviceProvider.GetTestFramework(), serviceProvider.GetTestApplicationProcessExitCode(), serviceProvider.GetLoggerFactory().CreateLogger())); - - builder.TestHost.AddDataConsumer(compositeHtmlReportGenerator); - builder.TestHost.AddTestSessionLifetimeHandler(compositeHtmlReportGenerator); - - builder.CommandLine.AddProvider(() => commandLine); - } } diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Microsoft.Testing.Extensions.HtmlReport.csproj b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Microsoft.Testing.Extensions.HtmlReport.csproj index f64a1df50c..dd146add10 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Microsoft.Testing.Extensions.HtmlReport.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Microsoft.Testing.Extensions.HtmlReport.csproj @@ -54,6 +54,7 @@ This package extends Microsoft Testing Platform to produce self-contained HTML t + diff --git a/src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportExtensions.cs index 49e8b5e44c..3f698fa68b 100644 --- a/src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportExtensions.cs @@ -4,7 +4,6 @@ using Microsoft.Testing.Extensions.JUnitReport; using Microsoft.Testing.Extensions.JUnitReport.Resources; using Microsoft.Testing.Platform.Builder; -using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Services; @@ -22,16 +21,11 @@ public static class JUnitReportExtensions /// /// The test application builder. public static void AddJUnitReportProvider(this ITestApplicationBuilder builder) - { - if (builder is not TestApplicationBuilder) - { - throw new InvalidOperationException(ExtensionResources.InvalidTestApplicationBuilderType); - } - - var commandLine = new JUnitReportGeneratorCommandLine(); - - var compositeJUnitReportGenerator = - new CompositeExtensionFactory(serviceProvider => + => ReportProviderRegistration.AddReportProvider( + builder, + ExtensionResources.InvalidTestApplicationBuilderType, + new JUnitReportGeneratorCommandLine(), + serviceProvider => new JUnitReportGenerator( serviceProvider.GetConfiguration(), serviceProvider.GetCommandLineOptions(), @@ -44,10 +38,4 @@ public static void AddJUnitReportProvider(this ITestApplicationBuilder builder) serviceProvider.GetTestFramework(), serviceProvider.GetTestApplicationProcessExitCode(), serviceProvider.GetLoggerFactory().CreateLogger())); - - builder.TestHost.AddDataConsumer(compositeJUnitReportGenerator); - builder.TestHost.AddTestSessionLifetimeHandler(compositeJUnitReportGenerator); - - builder.CommandLine.AddProvider(() => commandLine); - } } diff --git a/src/Platform/Microsoft.Testing.Extensions.JUnitReport/Microsoft.Testing.Extensions.JUnitReport.csproj b/src/Platform/Microsoft.Testing.Extensions.JUnitReport/Microsoft.Testing.Extensions.JUnitReport.csproj index ec11fa08e3..60823d340f 100644 --- a/src/Platform/Microsoft.Testing.Extensions.JUnitReport/Microsoft.Testing.Extensions.JUnitReport.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.JUnitReport/Microsoft.Testing.Extensions.JUnitReport.csproj @@ -52,6 +52,7 @@ This package extends Microsoft Testing Platform to produce JUnit XML test report + diff --git a/src/Platform/SharedExtensionHelpers/ReportProviderRegistration.cs b/src/Platform/SharedExtensionHelpers/ReportProviderRegistration.cs new file mode 100644 index 0000000000..95989443ed --- /dev/null +++ b/src/Platform/SharedExtensionHelpers/ReportProviderRegistration.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Extensions.TestHost; + +namespace Microsoft.Testing.Extensions; + +internal static class ReportProviderRegistration +{ + /// + /// Registers a report generator as both a data consumer and a test session lifetime handler, along with its + /// command-line options provider, applying the shared guard. + /// + /// The report generator type. + /// The test application builder. + /// + /// The error message used when is not a . + /// + /// The command-line options provider associated with the report. + /// The factory that creates the report generator from the service provider. + public static void AddReportProvider( + ITestApplicationBuilder builder, + string invalidBuilderTypeErrorMessage, + ICommandLineOptionsProvider commandLine, + Func generatorFactory) + where TGenerator : class, IDataConsumer, ITestSessionLifetimeHandler + { + if (builder is not TestApplicationBuilder) + { + throw new InvalidOperationException(invalidBuilderTypeErrorMessage); + } + + var compositeReportGenerator = new CompositeExtensionFactory(generatorFactory); + + builder.TestHost.AddDataConsumer(compositeReportGenerator); + builder.TestHost.AddTestSessionLifetimeHandler(compositeReportGenerator); + + builder.CommandLine.AddProvider(() => commandLine); + } +} From 580e3f5ed73c7b9d89914a4050c94f74acc8a9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 15 Jun 2026 15:22:22 +0200 Subject: [PATCH 2/2] Address review: lazily construct command-line provider after builder guard Convert AddReportProvider's commandLine parameter to a Func factory so the *ReportGeneratorCommandLine instance is only created after the TestApplicationBuilder guard passes, restoring the original short-circuit behavior. Also replace internal-type cref docs with prose. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../CtrfReportExtensions.cs | 2 +- .../HtmlReportExtensions.cs | 2 +- .../JUnitReportExtensions.cs | 2 +- .../SharedExtensionHelpers/ReportProviderRegistration.cs | 9 +++++---- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Platform/Microsoft.Testing.Extensions.CtrfReport/CtrfReportExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.CtrfReport/CtrfReportExtensions.cs index 01485c02fa..bdc1c73807 100644 --- a/src/Platform/Microsoft.Testing.Extensions.CtrfReport/CtrfReportExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.CtrfReport/CtrfReportExtensions.cs @@ -24,7 +24,7 @@ public static void AddCtrfReportProvider(this ITestApplicationBuilder builder) => ReportProviderRegistration.AddReportProvider( builder, ExtensionResources.InvalidTestApplicationBuilderType, - new CtrfReportGeneratorCommandLine(), + () => new CtrfReportGeneratorCommandLine(), serviceProvider => new CtrfReportGenerator( serviceProvider.GetConfiguration(), diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportExtensions.cs index 41e5edf110..2d8b0c7327 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportExtensions.cs @@ -24,7 +24,7 @@ public static void AddHtmlReportProvider(this ITestApplicationBuilder builder) => ReportProviderRegistration.AddReportProvider( builder, ExtensionResources.InvalidTestApplicationBuilderType, - new HtmlReportGeneratorCommandLine(), + () => new HtmlReportGeneratorCommandLine(), serviceProvider => new HtmlReportGenerator( serviceProvider.GetConfiguration(), diff --git a/src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportExtensions.cs index 3f698fa68b..ae81b19b13 100644 --- a/src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportExtensions.cs @@ -24,7 +24,7 @@ public static void AddJUnitReportProvider(this ITestApplicationBuilder builder) => ReportProviderRegistration.AddReportProvider( builder, ExtensionResources.InvalidTestApplicationBuilderType, - new JUnitReportGeneratorCommandLine(), + () => new JUnitReportGeneratorCommandLine(), serviceProvider => new JUnitReportGenerator( serviceProvider.GetConfiguration(), diff --git a/src/Platform/SharedExtensionHelpers/ReportProviderRegistration.cs b/src/Platform/SharedExtensionHelpers/ReportProviderRegistration.cs index 95989443ed..6f1b8f5ee3 100644 --- a/src/Platform/SharedExtensionHelpers/ReportProviderRegistration.cs +++ b/src/Platform/SharedExtensionHelpers/ReportProviderRegistration.cs @@ -12,19 +12,19 @@ internal static class ReportProviderRegistration { /// /// Registers a report generator as both a data consumer and a test session lifetime handler, along with its - /// command-line options provider, applying the shared guard. + /// command-line options provider, applying the shared TestApplicationBuilder implementation guard. /// /// The report generator type. /// The test application builder. /// - /// The error message used when is not a . + /// The error message used when is not a TestApplicationBuilder. /// - /// The command-line options provider associated with the report. + /// The factory that creates the command-line options provider associated with the report. /// The factory that creates the report generator from the service provider. public static void AddReportProvider( ITestApplicationBuilder builder, string invalidBuilderTypeErrorMessage, - ICommandLineOptionsProvider commandLine, + Func commandLineFactory, Func generatorFactory) where TGenerator : class, IDataConsumer, ITestSessionLifetimeHandler { @@ -38,6 +38,7 @@ public static void AddReportProvider( builder.TestHost.AddDataConsumer(compositeReportGenerator); builder.TestHost.AddTestSessionLifetimeHandler(compositeReportGenerator); + ICommandLineOptionsProvider commandLine = commandLineFactory(); builder.CommandLine.AddProvider(() => commandLine); } }