From 88352c98d535c73c415bbb7b6ed28ee9f3c7129a Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski <70535775+BartoszKlonowski@users.noreply.github.com> Date: Fri, 5 Jun 2026 15:14:01 +0200 Subject: [PATCH 1/5] Fix the formatting of the `ar-SA` culture date in "Working with calendars" (#53877) * Fix the formatting of the ar-SA culture calendar display * Adjust the example output comment * Briefly mention the fact of setting Calendar for culture change * Do not explicitly set the calendar when changing for ar-SA * Revert commits 152fac48..276462d8 * Set the encoding to UTF8 in example to display arabic letters * Adjust wording to style guide * Move snippets * Modernize code --------- Co-authored-by: Andy De George (from Dev Box) --- .../csharp/ChangeCalendar.csproj | 10 +++ .../working-with-calendars/csharp/Program.cs | 51 +++++++++++++++ .../vb/ChangeCalendar.vbproj | 9 +++ .../working-with-calendars/vb/Program.vb | 29 +++------ .../datetime/working-with-calendars.md | 6 +- .../cs/changecalendar2.cs | 63 ------------------- 6 files changed, 84 insertions(+), 84 deletions(-) create mode 100644 docs/standard/datetime/snippets/working-with-calendars/csharp/ChangeCalendar.csproj create mode 100644 docs/standard/datetime/snippets/working-with-calendars/csharp/Program.cs create mode 100644 docs/standard/datetime/snippets/working-with-calendars/vb/ChangeCalendar.vbproj rename samples/snippets/visualbasic/VS_Snippets_CLR/conceptual.calendars/vb/changecalendar2.vb => docs/standard/datetime/snippets/working-with-calendars/vb/Program.vb (65%) delete mode 100644 samples/snippets/csharp/VS_Snippets_CLR/conceptual.calendars/cs/changecalendar2.cs diff --git a/docs/standard/datetime/snippets/working-with-calendars/csharp/ChangeCalendar.csproj b/docs/standard/datetime/snippets/working-with-calendars/csharp/ChangeCalendar.csproj new file mode 100644 index 0000000000000..ed9781c223ab9 --- /dev/null +++ b/docs/standard/datetime/snippets/working-with-calendars/csharp/ChangeCalendar.csproj @@ -0,0 +1,10 @@ + + + + Exe + net10.0 + enable + enable + + + diff --git a/docs/standard/datetime/snippets/working-with-calendars/csharp/Program.cs b/docs/standard/datetime/snippets/working-with-calendars/csharp/Program.cs new file mode 100644 index 0000000000000..931e92115fd7e --- /dev/null +++ b/docs/standard/datetime/snippets/working-with-calendars/csharp/Program.cs @@ -0,0 +1,51 @@ +// +using System.Globalization; + +DateTime date1 = new(2011, 6, 20); + +Console.OutputEncoding = System.Text.Encoding.UTF8; +DisplayCurrentInfo(); +// Display the date using the current culture and calendar. +Console.WriteLine(date1.ToString("d")); +Console.WriteLine(); + +CultureInfo arSA = CultureInfo.CreateSpecificCulture("ar-SA"); + +// Change the current culture to Arabic (Saudi Arabia). +CultureInfo.CurrentCulture = arSA; +// Display date and information about the current culture. +DisplayCurrentInfo(); +Console.WriteLine(date1.ToString("d")); +Console.WriteLine(); + +// Change the calendar to Hijri. +Calendar hijri = new HijriCalendar(); +if (CalendarExists(arSA, hijri)) +{ + arSA.DateTimeFormat.Calendar = hijri; + // Display date and information about the current culture. + DisplayCurrentInfo(); + Console.WriteLine(date1.ToString("d")); +} + +static void DisplayCurrentInfo() +{ + Console.WriteLine($"Current Culture: {CultureInfo.CurrentCulture.Name}"); + Console.WriteLine($"Current Calendar: {DateTimeFormatInfo.CurrentInfo.Calendar}"); +} + +static bool CalendarExists(CultureInfo culture, Calendar cal) => + culture.OptionalCalendars.Any(optional => optional.ToString() == cal.ToString()); +// The example displays the following output: +// Current Culture: en-US +// Current Calendar: System.Globalization.GregorianCalendar +// 6/20/2011 +// +// Current Culture: ar-SA +// Current Calendar: System.Globalization.UmAlQuraCalendar +// 18‏‏/7‏‏/1432 بعد الهجرة +// +// Current Culture: ar-SA +// Current Calendar: System.Globalization.HijriCalendar +// 19‏‏/7‏‏/1432 بعد الهجرة +// diff --git a/docs/standard/datetime/snippets/working-with-calendars/vb/ChangeCalendar.vbproj b/docs/standard/datetime/snippets/working-with-calendars/vb/ChangeCalendar.vbproj new file mode 100644 index 0000000000000..7244b18a8a7c9 --- /dev/null +++ b/docs/standard/datetime/snippets/working-with-calendars/vb/ChangeCalendar.vbproj @@ -0,0 +1,9 @@ + + + + Exe + ChangeCalendar + net10.0 + + + diff --git a/samples/snippets/visualbasic/VS_Snippets_CLR/conceptual.calendars/vb/changecalendar2.vb b/docs/standard/datetime/snippets/working-with-calendars/vb/Program.vb similarity index 65% rename from samples/snippets/visualbasic/VS_Snippets_CLR/conceptual.calendars/vb/changecalendar2.vb rename to docs/standard/datetime/snippets/working-with-calendars/vb/Program.vb index 3de919e3ad1ab..20fe05507d683 100644 --- a/samples/snippets/visualbasic/VS_Snippets_CLR/conceptual.calendars/vb/changecalendar2.vb +++ b/docs/standard/datetime/snippets/working-with-calendars/vb/Program.vb @@ -1,14 +1,11 @@ -' Visual Basic .NET Document -Option Strict On - -' +' Imports System.Globalization -Imports System.Threading Module Example Public Sub Main() Dim date1 As Date = #6/20/2011# + Console.OutputEncoding = System.Text.Encoding.UTF8 DisplayCurrentInfo() ' Display the date using the current culture and calendar. Console.WriteLine(date1.ToString("d")) @@ -17,7 +14,7 @@ Module Example Dim arSA As CultureInfo = CultureInfo.CreateSpecificCulture("ar-SA") ' Change the current culture to Arabic (Saudi Arabia). - Thread.CurrentThread.CurrentCulture = arSA + CultureInfo.CurrentCulture = arSA ' Display date and information about the current culture. DisplayCurrentInfo() Console.WriteLine(date1.ToString("d")) @@ -34,18 +31,12 @@ Module Example End Sub Private Sub DisplayCurrentInfo() - Console.WriteLine("Current Culture: {0}", - CultureInfo.CurrentCulture.Name) - Console.WriteLine("Current Calendar: {0}", - DateTimeFormatInfo.CurrentInfo.Calendar) + Console.WriteLine($"Current Culture: {CultureInfo.CurrentCulture.Name}") + Console.WriteLine($"Current Calendar: {DateTimeFormatInfo.CurrentInfo.Calendar}") End Sub - Private Function CalendarExists(ByVal culture As CultureInfo, - cal As Calendar) As Boolean - For Each optionalCalendar As Calendar In culture.OptionalCalendars - If cal.ToString().Equals(optionalCalendar.ToString()) Then Return True - Next - Return False + Private Function CalendarExists(culture As CultureInfo, cal As Calendar) As Boolean + Return culture.OptionalCalendars.Any(Function(optional1) optional1.ToString() = cal.ToString()) End Function End Module ' The example displays the following output: @@ -55,9 +46,9 @@ End Module ' ' Current Culture: ar-SA ' Current Calendar: System.Globalization.UmAlQuraCalendar -' 18/07/32 +' 18‏‏/7‏‏/1432 بعد الهجرة ' ' Current Culture: ar-SA ' Current Calendar: System.Globalization.HijriCalendar -' 19/07/32 -' +' 19‏‏/7‏‏/1432 بعد الهجرة +' diff --git a/docs/standard/datetime/working-with-calendars.md b/docs/standard/datetime/working-with-calendars.md index 6b6c6767852d2..f2d1928571114 100644 --- a/docs/standard/datetime/working-with-calendars.md +++ b/docs/standard/datetime/working-with-calendars.md @@ -71,8 +71,10 @@ The calendar currently in use by a particular value and displays it using the current culture - which, in this case, is English (United States) - and the current culture's calendar (which, in this case, is the Gregorian calendar). Next, it changes the current culture to Arabic (Saudi Arabia) and displays the date using its default Um Al-Qura calendar. It then calls the `CalendarExists` method to determine whether the Hijri calendar is supported by the Arabic (Saudi Arabia) culture. Because the calendar is supported, it changes the current calendar to Hijri and again displays the date. Note that in each case, the date is displayed using the current culture's current calendar. -[!code-csharp[Conceptual.Calendars#2](../../../samples/snippets/csharp/VS_Snippets_CLR/conceptual.calendars/cs/changecalendar2.cs#2)] -[!code-vb[Conceptual.Calendars#2](../../../samples/snippets/visualbasic/VS_Snippets_CLR/conceptual.calendars/vb/changecalendar2.vb#2)] +:::code language="csharp" source="./snippets/working-with-calendars/csharp/Program.cs" id="ChangeCalendar"::: +:::code language="vb" source="./snippets/working-with-calendars/vb/Program.vb" id="ChangeCalendar"::: + +Before the example writes to the console, it changes the console's output encoding to UTF-8 so that Arabic letters display correctly. ## Dates and calendars diff --git a/samples/snippets/csharp/VS_Snippets_CLR/conceptual.calendars/cs/changecalendar2.cs b/samples/snippets/csharp/VS_Snippets_CLR/conceptual.calendars/cs/changecalendar2.cs deleted file mode 100644 index 87a600d442d67..0000000000000 --- a/samples/snippets/csharp/VS_Snippets_CLR/conceptual.calendars/cs/changecalendar2.cs +++ /dev/null @@ -1,63 +0,0 @@ -// -using System; -using System.Globalization; -using System.Threading; - -public class Example -{ - public static void Main() - { - DateTime date1 = new DateTime(2011, 6, 20); - - DisplayCurrentInfo(); - // Display the date using the current culture and calendar. - Console.WriteLine(date1.ToString("d")); - Console.WriteLine(); - - CultureInfo arSA = CultureInfo.CreateSpecificCulture("ar-SA"); - - // Change the current culture to Arabic (Saudi Arabia). - Thread.CurrentThread.CurrentCulture = arSA; - // Display date and information about the current culture. - DisplayCurrentInfo(); - Console.WriteLine(date1.ToString("d")); - Console.WriteLine(); - - // Change the calendar to Hijri. - Calendar hijri = new HijriCalendar(); - if (CalendarExists(arSA, hijri)) { - arSA.DateTimeFormat.Calendar = hijri; - // Display date and information about the current culture. - DisplayCurrentInfo(); - Console.WriteLine(date1.ToString("d")); - } - } - - private static void DisplayCurrentInfo() - { - Console.WriteLine($"Current Culture: {CultureInfo.CurrentCulture.Name}"); - Console.WriteLine($"Current Calendar: {DateTimeFormatInfo.CurrentInfo.Calendar}"); - } - - private static bool CalendarExists(CultureInfo culture, Calendar cal) - { - foreach (Calendar optionalCalendar in culture.OptionalCalendars) - if (cal.ToString().Equals(optionalCalendar.ToString())) - return true; - - return false; - } -} -// The example displays the following output: -// Current Culture: en-US -// Current Calendar: System.Globalization.GregorianCalendar -// 6/20/2011 -// -// Current Culture: ar-SA -// Current Calendar: System.Globalization.UmAlQuraCalendar -// 18/07/32 -// -// Current Culture: ar-SA -// Current Calendar: System.Globalization.HijriCalendar -// 19/07/32 -// From 81934c53080f4fca873429b8ea7c19b2c16d5deb Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Fri, 5 Jun 2026 06:27:08 -0700 Subject: [PATCH 2/5] Change title "Objected oriented programming" to "Object oriented programming" (#54191) * Change title "Objected oriented programming" to "Object oriented programming" * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Bill Wagner Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/csharp/fundamentals/object-oriented/inheritance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/fundamentals/object-oriented/inheritance.md b/docs/csharp/fundamentals/object-oriented/inheritance.md index da167fae69845..7dce171817ebb 100644 --- a/docs/csharp/fundamentals/object-oriented/inheritance.md +++ b/docs/csharp/fundamentals/object-oriented/inheritance.md @@ -1,5 +1,5 @@ --- -title: "Objected oriented programming - inheritance" +title: "Object-oriented programming - inheritance" description: Inheritance in C# enables you to create new classes that reuse, extend, and modify the behavior defined in other classes. ms.date: 05/14/2021 helpviewer_keywords: From 661b39596ccb1ed2662aa9f7408de34ae68dc4e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Fri, 5 Jun 2026 15:27:24 +0200 Subject: [PATCH 3/5] Document dotnet test (MTP) argument forwarding caveat (#54192) * Document dotnet test (MTP) argument forwarding caveat When dotnet test consumes a recognized option that appears between an unrecognized option name and a value, the leftover tokens passed to the test application can change meaning. Document this behavior and recommend -- as a separator to mark test application arguments explicitly. Addresses https://github.com/dotnet/sdk/issues/51990. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Move argument-forwarding caveat to dotnet run docs Address review feedback: - @Youssef1313: move the detailed explanation of the parser caveat to `dotnet run` (where the behavior originates) and have `dotnet test` (MTP mode) link to it. This avoids giving the impression that the issue is specific to MTP. - Copilot: split the TIP in unit-testing-with-dotnet-test.md into shorter sentences and remove the repeated `it`. - Copilot: add `ai-usage: ai-assisted` to the unit-testing-with-dotnet-test.md frontmatter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../testing/unit-testing-with-dotnet-test.md | 6 ++++- docs/core/tools/dotnet-run.md | 25 ++++++++++++++++++- docs/core/tools/dotnet-test-mtp.md | 12 ++++++++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/docs/core/testing/unit-testing-with-dotnet-test.md b/docs/core/testing/unit-testing-with-dotnet-test.md index 1f6f17d39c8cd..369e6bc4980e0 100644 --- a/docs/core/testing/unit-testing-with-dotnet-test.md +++ b/docs/core/testing/unit-testing-with-dotnet-test.md @@ -3,7 +3,8 @@ title: Testing with 'dotnet test' description: Learn more about how 'dotnet test' works and its support for VSTest and Microsoft.Testing.Platform (MTP) author: Youssef1313 ms.author: ygerges -ms.date: 03/26/2025 +ms.date: 06/05/2026 +ai-usage: ai-assisted --- # Testing with 'dotnet test' @@ -158,6 +159,9 @@ For users of MTP that are using the VSTest mode of `dotnet test`, there are few 1. If passing a specific project (or directory containing project), for example, `dotnet test MyProject.csproj`, this should become `dotnet test --project MyProject.csproj`. 1. If passing a specific dll, for example, `dotnet test path/to/UnitTests.dll`, this should become `dotnet test --test-modules path/to/UnitTests.dll`. Note that `--test-modules` also supports globbing. +> [!TIP] +> Even though `--` is no longer required in MTP mode, you can still use it to mark where test application arguments begin. The separator avoids a parser quirk where unrecognized arguments change meaning when interleaved with options that `dotnet test` understands. The separator also protects your scripts if `dotnet test` later starts recognizing one of those tokens. For more information, see [Forward arguments to the test application](../tools/dotnet-test-mtp.md#forward-arguments-to-the-test-application). + ## Solutions with mixed test frameworks or extensions When a solution contains test projects that use different test frameworks (for example, MSTest and xUnit.net) or different sets of extensions, running `dotnet test` with framework-specific or extension-specific command-line options can fail. Options that are valid for one project are unrecognized by another, causing exit code 5 (invalid command-line arguments). For example: diff --git a/docs/core/tools/dotnet-run.md b/docs/core/tools/dotnet-run.md index 44d165524d651..62d77bde2a3ef 100644 --- a/docs/core/tools/dotnet-run.md +++ b/docs/core/tools/dotnet-run.md @@ -1,7 +1,7 @@ --- title: dotnet run command description: The dotnet run command provides a convenient option to run your application from the source code. -ms.date: 09/29/2025 +ms.date: 06/05/2026 --- # dotnet run @@ -61,6 +61,29 @@ To run the application, the `dotnet run` command resolves the dependencies of th Any arguments that aren't recognized by `dotnet run` are passed to the application. To separate arguments for `dotnet run` from arguments for the application, use the `--` option. +## Forward arguments to the application + +`dotnet run` forwards any token it doesn't recognize to the application. The forwarded tokens keep their original order, but `dotnet run` first removes the options it understands. When a recognized option appears between an unrecognized option name and its value, removing the recognized option can change the meaning of the leftover tokens. + +For example, the following command interleaves the recognized option `--project` between tokens the application is meant to receive: + +```dotnetcli +dotnet run --app-flag --app-name --project ConsoleApp.csproj A.txt +``` + +After `dotnet run` consumes `--project ConsoleApp.csproj`, the application receives `--app-flag --app-name A.txt`. The application then treats `A.txt` as the value of `--app-name`, which doesn't match the original command line. + +To avoid this ambiguity, place application arguments after a literal `--`: + +```dotnetcli +dotnet run --project ConsoleApp.csproj -- --app-flag --app-name A.txt +``` + +The `--` separator marks every following token as an application argument, so `dotnet run` doesn't reorder or reinterpret them. The separator also future-proofs scripts against new `dotnet run` options that might later match a token previously forwarded to the application. + +> [!NOTE] +> The same behavior applies to `dotnet build` and to `dotnet test` in Microsoft.Testing.Platform (MTP) mode, which forward unrecognized tokens to MSBuild or to the test application respectively. For more information about `dotnet test`, see [Forward arguments to the test application](dotnet-test-mtp.md#forward-arguments-to-the-test-application). + ## Options - **`--`** diff --git a/docs/core/tools/dotnet-test-mtp.md b/docs/core/tools/dotnet-test-mtp.md index ab825cebe4557..73305235510e6 100644 --- a/docs/core/tools/dotnet-test-mtp.md +++ b/docs/core/tools/dotnet-test-mtp.md @@ -1,7 +1,7 @@ --- title: dotnet test command with Microsoft.Testing.Platform (MTP) description: The dotnet test command is used to execute unit tests in a given project using MTP. -ms.date: 02/03/2026 +ms.date: 06/05/2026 ai-usage: ai-assisted --- # dotnet test with Microsoft.Testing.Platform (MTP) @@ -168,6 +168,16 @@ With MTP, `dotnet test` operates faster than with VSTest. The test-related argum > [!NOTE] > To enable trace logging to a file, use the environment variable `DOTNET_CLI_TEST_TRACEFILE` to provide the path to the trace file. +## Forward arguments to the test application + +`dotnet test` forwards any token it doesn't recognize to the test application. When a recognized option appears between an unrecognized option name and its value, removing the recognized option can change how the leftover tokens bind to options in the test application. To avoid this ambiguity, place test application arguments after a literal `--`: + +```dotnetcli +dotnet test --results-directory TestResults -- --report-trx --report-trx-filename A.trx +``` + +The same parser behavior applies to `dotnet run` and `dotnet build`. For a detailed example, see [Forward arguments to the application](dotnet-run.md#forward-arguments-to-the-application) in the `dotnet run` reference. + ## Examples - Run the tests in the project or solution in the current directory: From f016f40c10230197f910ed7ba1865a0e7dc78c2d Mon Sep 17 00:00:00 2001 From: Omair Majid Date: Fri, 5 Jun 2026 11:48:00 -0400 Subject: [PATCH 4/5] Add deterministic dotnet pack info (#54196) For more context, see: - https://github.com/NuGet/Home/blob/dev/accepted/2026/deterministic-pack-revisited.md - https://github.com/NuGet/Home/issues/8601 - https://github.com/NuGet/NuGet.Client/pull/7020 --- docs/core/tools/dotnet-pack.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/core/tools/dotnet-pack.md b/docs/core/tools/dotnet-pack.md index 3d9cfeec0266a..d996a16fc9ba8 100644 --- a/docs/core/tools/dotnet-pack.md +++ b/docs/core/tools/dotnet-pack.md @@ -211,6 +211,12 @@ By default, `dotnet pack` builds the project first. If you wish to avoid this be dotnet pack --runtime win-x64 ``` +- Pack the project in the current directory into a deterministic package (.NET 10.0.400 and later): + + ```dotnetcli + dotnet pack -p:Deterministic=true -p:DeterministicTimestamp="2026-12-19T16:39:57-08:00" + ``` + - Pack the project using a *.nuspec* file (MSBuild project-based approach): ```dotnetcli From 186534e699f82ca45243237df57412078adb18e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Fri, 5 Jun 2026 18:46:41 +0200 Subject: [PATCH 5/5] Document TestingPlatformBuilderHook auto-registration for MTP extension authors (#54122) * Document TestingPlatformBuilderHook auto-registration for MTP extension authors Addresses microsoft/testfx#5552 by adding a new section to the MTP extensions architecture article that explains: - How extension authors expose a TestingPlatformBuilderHook class and ship a buildMultiTargeting props file so consumers don't need manual registration. - The required AddExtensions(ITestApplicationBuilder, string[]) method signature and the required MSBuild item metadata (Include, DisplayName, TypeFullName). - That the Include GUID is a freshly generated, random identifier that must not be reused from any other extension (with an IMPORTANT callout) and is distinct from IExtension.Uid. - How to verify the hook is wired up by inspecting the generated SelfRegisteredExtensions.g.cs file. Also adds a TIP at the top of the Extensibility points section pointing readers to the new section, and bumps ms.date. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- ...esting-platform-architecture-extensions.md | 92 ++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/docs/core/testing/microsoft-testing-platform-architecture-extensions.md b/docs/core/testing/microsoft-testing-platform-architecture-extensions.md index 6ea28cb1e72a5..4bcd1417fd33a 100644 --- a/docs/core/testing/microsoft-testing-platform-architecture-extensions.md +++ b/docs/core/testing/microsoft-testing-platform-architecture-extensions.md @@ -3,7 +3,7 @@ title: Build extensions for Microsoft.Testing.Platform (MTP) description: Learn how to create in-process and out-of-process extensions for Microsoft.Testing.Platform (MTP). author: MarcoRossignoli ms.author: mrossignoli -ms.date: 02/24/2026 +ms.date: 06/02/2026 ai-usage: ai-assisted --- @@ -17,6 +17,9 @@ For the full extension point summary and in-process/out-of-process concepts, see The testing platform provides additional extensibility points that allow you to customize the behavior of the platform and the test framework. These extensibility points are optional and can be used to enhance the testing experience. +> [!TIP] +> Each extension shown in this article includes a manual registration snippet (for example, `builder.TestHost.AddDataConsumer(...)`). If you're shipping your extension as a NuGet package, you can let consumers skip the manual call by exposing a `TestingPlatformBuilderHook` and a small MSBuild props file. The auto-generated entry point will then invoke your hook automatically. For details, see [Auto-register your extension with `TestingPlatformBuilderHook`](#auto-register-your-extension-with-testingplatformbuilderhook). + ### The `ICommandLineOptionsProvider` extensions > [!NOTE] @@ -499,6 +502,93 @@ The `ITestHostProcessInformation` interface provides the following details: * `ExitCode`: The exit code of the process. This value is only available within the `OnTestHostProcessExitedAsync` method. Attempting to access it within the `OnTestHostProcessStartedAsync` method will result in an exception. * `HasExitedGracefully`: A boolean value indicating whether the test host has crashed. If true, it signifies that the test host did not exit gracefully. +## Auto-register your extension with `TestingPlatformBuilderHook` + +Every preceding extension section shows a *manual* registration call (for example, `builder.TestHost.AddDataConsumer(...)`). Asking consumers to edit their `Main` method is a poor onboarding experience. The [`Microsoft.Testing.Platform.MSBuild`](https://www.nuget.org/packages/Microsoft.Testing.Platform.MSBuild) package solves this by generating a `SelfRegisteredExtensions.AddSelfRegisteredExtensions(builder, args)` method that runs from the autogenerated entry point. To plug your extension into that generated method, ship two artifacts in your NuGet package: + +- A public static `TestingPlatformBuilderHook` class with an `AddExtensions` method that registers your extension. +- An MSBuild props file that declares a `` item pointing at that class. + +When a consumer installs your package, the MSBuild integration picks up the item and generates the call into your hook, and your extension is registered with no code changes on the consumer side. + +> [!NOTE] +> Auto-registration only works when the consumer has `Microsoft.Testing.Platform.MSBuild` in their project (it's included transitively by MSTest, NUnit, and xUnit runners) and hasn't opted out by setting `false`. Consumers who disable the autogenerated entry point still need to call your manual registration API from their `Main` method. + +### Create the hook class + +Add a `public static class TestingPlatformBuilderHook` in your extension assembly with an `AddExtensions(ITestApplicationBuilder, string[])` method that performs the same registration users would otherwise call manually: + +```csharp +using Microsoft.Testing.Platform.Builder; + +namespace Contoso.MyExtension; + +public static class TestingPlatformBuilderHook +{ + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] arguments) + => testApplicationBuilder.AddMyExtension(); +} +``` + +The class name doesn't have to be `TestingPlatformBuilderHook` — the MSBuild item points at it by full type name — but using that name keeps your code consistent with the in-box extensions like `Microsoft.Testing.Extensions.Retry` and `Microsoft.Testing.Extensions.HotReload`. + +The method must: + +* Be `public static`. +* Have a first parameter of type `Microsoft.Testing.Platform.Builder.ITestApplicationBuilder`. +* Have a second parameter of type `string[]` (the command-line arguments passed to the test host). You can ignore it if your extension doesn't need it. +* Return `void`. + +### Declare the MSBuild item + +Ship a props file under `buildMultiTargeting/.props` inside your NuGet package. Declare a `` item that points the MSBuild task at your hook class: + +```xml + + + + Contoso.MyExtension + Contoso.MyExtension.TestingPlatformBuilderHook + + + +``` + +The metadata is as follows: + +* `Include`: A GUID that uniquely identifies your hook. See [The `Include` GUID is a random identifier](#the-include-guid-is-a-random-identifier). +* `DisplayName`: The friendly name shown in MSBuild diagnostic messages when the entry point is generated. Use your package or extension name. +* `TypeFullName`: The fully qualified name of the `TestingPlatformBuilderHook` class you created previously. The MSBuild task uses this to emit `global::Contoso.MyExtension.TestingPlatformBuilderHook.AddExtensions(builder, args);` into the generated entry point. + +### The `Include` GUID is a random identifier + +The GUID in the `Include` attribute is **not** the same as your extension's [`IExtension.Uid`](./microsoft-testing-platform-architecture.md#the-iextension-interface). It's a registration identifier used by the MSBuild task to deduplicate hooks across NuGet references and (in a few well-known cases) to order them. + +When you author a new extension, generate a brand new GUID and hard-code it in your props file. Some ways to generate one: + +* PowerShell: `[guid]::NewGuid()` +* Visual Studio: **Tools** > **Create GUID** +* `uuidgen` on Linux and macOS + +> [!IMPORTANT] +> Never copy a GUID from another extension's props file (whether shipped by Microsoft or by a third party). Two extensions that share the same `Include` value are treated as duplicates: only one hook is invoked, so your extension silently fails to register. + +> [!NOTE] +> Once you ship a GUID, treat it as permanent. Changing it in a later release is harmless on its own, but reusing the *old* value for a different hook in a future package version can confuse consumers who have both versions in their dependency graph during an upgrade. + +### Verify the hook is wired up + +After installing your package in a test project that uses `Microsoft.Testing.Platform.MSBuild`, build the project and inspect the generated `SelfRegisteredExtensions.g.cs` file under `obj///`. You should see a call into your hook, for example: + +```csharp +public static void AddSelfRegisteredExtensions(this global::Microsoft.Testing.Platform.Builder.ITestApplicationBuilder builder, string[] args) +{ + global::Contoso.MyExtension.TestingPlatformBuilderHook.AddExtensions(builder, args); +} +``` + +If the call is missing, double-check that the props file is packaged under `buildMultiTargeting/` (not `build/`) inside the `.nupkg`, that `DisplayName` and `TypeFullName` metadata are present, and that the consumer hasn't set `false`. + ## Extensions execution order The testing platform consists of a [testing framework](./microsoft-testing-platform-architecture-test-framework.md#test-framework-extension) and any number of extensions that can operate [*in-process*](./microsoft-testing-platform-architecture.md#in-process-vs-out-of-process-extensions) or [*out-of-process*](./microsoft-testing-platform-architecture.md#in-process-vs-out-of-process-extensions). This document outlines the **sequence of calls** to all potential extensibility points to provide clarity on when a feature is anticipated to be invoked: