From 930bb0fb0c27be288527a2a2d3e3326ffc7e1b1d Mon Sep 17 00:00:00 2001 From: Evangelink Date: Fri, 19 Jun 2026 19:30:30 +0200 Subject: [PATCH 1/2] Make AzureDevOps summary report file name unique per assembly The Azure DevOps job summary reporter used a default file name keyed only by target framework (azdo-summary-{tfm}.md). When multiple test assemblies run in parallel into the same TestResults directory (a common CI setup) they all resolved to the same path and raced to write it, producing the pipeline warning 'The process cannot access the file ... because it is being used by another process'. Align the default name with the __ convention already used by the TRX/HTML/JUnit reports so each test assembly writes a unique file: azdo-summary-{assembly}-{tfm}-{arch}.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../AzureDevOpsSummaryReporter.cs | 16 ++++++++++++++-- ...t.Testing.Extensions.AzureDevOpsReport.csproj | 1 + .../Resources/AzureDevOpsResources.resx | 4 ++-- .../Resources/xlf/AzureDevOpsResources.cs.xlf | 6 +++--- .../Resources/xlf/AzureDevOpsResources.de.xlf | 6 +++--- .../Resources/xlf/AzureDevOpsResources.es.xlf | 6 +++--- .../Resources/xlf/AzureDevOpsResources.fr.xlf | 6 +++--- .../Resources/xlf/AzureDevOpsResources.it.xlf | 6 +++--- .../Resources/xlf/AzureDevOpsResources.ja.xlf | 6 +++--- .../Resources/xlf/AzureDevOpsResources.ko.xlf | 6 +++--- .../Resources/xlf/AzureDevOpsResources.pl.xlf | 6 +++--- .../Resources/xlf/AzureDevOpsResources.pt-BR.xlf | 6 +++--- .../Resources/xlf/AzureDevOpsResources.ru.xlf | 6 +++--- .../Resources/xlf/AzureDevOpsResources.tr.xlf | 6 +++--- .../xlf/AzureDevOpsResources.zh-Hans.xlf | 6 +++--- .../xlf/AzureDevOpsResources.zh-Hant.xlf | 6 +++--- .../HelpInfoAllExtensionsTests.cs | 4 ++-- .../AzureDevOpsSummaryReporterTests.cs | 3 +++ 18 files changed, 61 insertions(+), 45 deletions(-) diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsSummaryReporter.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsSummaryReporter.cs index b8f8ab6858..8f7e5dbf57 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsSummaryReporter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsSummaryReporter.cs @@ -19,7 +19,7 @@ namespace Microsoft.Testing.Extensions.AzureDevOpsReport; internal sealed class AzureDevOpsSummaryReporter : IDataConsumer, ITestSessionLifetimeHandler, IOutputDeviceDataProducer { - private const string DefaultSummaryFileNameFormat = "azdo-summary-{0}.md"; + private const string DefaultSummaryFileNameFormat = "azdo-summary-{0}-{1}-{2}.md"; private const string FullyQualifiedNamePropertyKey = "vstest.TestCase.FullyQualifiedName"; private const int MaxSlowestTests = 10; private const int MaxTopFailingClasses = 5; @@ -266,7 +266,19 @@ public async Task OnTestSessionFinishingAsync(ITestSessionContext testSessionCon return null; } - string fileName = string.Format(CultureInfo.InvariantCulture, DefaultSummaryFileNameFormat, _targetFrameworkMoniker.Value); + // Include the assembly name and process architecture in the default file name (matching the + // __ shape used by the TRX/HTML/JUnit reports) so that multiple test assemblies + // that share the same target framework and TestResults directory (a common CI setup) don't all + // resolve to the same path and race to write it, which surfaced as + // "The process cannot access the file ... because it is being used by another process". + string assemblyName = _testApplicationModuleInfo.TryGetAssemblyName() ?? "unknown"; + string architecture = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); + string fileName = string.Format( + CultureInfo.InvariantCulture, + DefaultSummaryFileNameFormat, + ReportFileNameSanitizer.ReplaceInvalidFileNameChars(assemblyName), + _targetFrameworkMoniker.Value, + architecture); return Path.GetFullPath(Path.Combine(configuredTestResultsDirectory!, fileName)); } diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj index eb863f0940..e9a3e00d87 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj @@ -12,6 +12,7 @@ + diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx index 9e21aae3e9..838520836c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx @@ -326,8 +326,8 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf index b10da00025..10784250b1 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - Na konci testovacího běhu napište souhrn úlohy v Markdownu a nahrajte ho přes ##vso[task.uploadsummary]. Volitelný argument cesty k souboru přepíše výchozí umístění ({testResultsDir}/azdo-summary-{tfm}.md). Vyžaduje --report-azdo. - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + Na konci testovacího běhu napište souhrn úlohy v Markdownu a nahrajte ho přes ##vso[task.uploadsummary]. Volitelný argument cesty k souboru přepíše výchozí umístění ({testResultsDir}/azdo-summary-{tfm}.md). Vyžaduje --report-azdo. + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf index 216a053da5..9e6f24a579 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - Schreiben Sie am Ende des Testlaufs eine Markdownauftragszusammenfassung, und laden Sie sie über „##vso[task.uploadsummary]“ hoch. Ein optionales Dateipfadargument überschreibt den Standardspeicherort („{testResultsDir}/azdo-summary-{tfm}.md“). Erfordert „--report-azdo“. - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + Schreiben Sie am Ende des Testlaufs eine Markdownauftragszusammenfassung, und laden Sie sie über „##vso[task.uploadsummary]“ hoch. Ein optionales Dateipfadargument überschreibt den Standardspeicherort („{testResultsDir}/azdo-summary-{tfm}.md“). Erfordert „--report-azdo“. + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf index def1b7213f..3e68a1b9e5 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - Escriba un resumen del trabajo de Markdown al final de la serie de pruebas y cárguelo a través de "##vso[task.uploadsummary]". Un argumento de ruta de acceso de archivo opcional invalida la ubicación predeterminada ("{testResultsDir}/azdo-summary-{tfm}.md"). Requiere "--report-azdo". - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + Escriba un resumen del trabajo de Markdown al final de la serie de pruebas y cárguelo a través de "##vso[task.uploadsummary]". Un argumento de ruta de acceso de archivo opcional invalida la ubicación predeterminada ("{testResultsDir}/azdo-summary-{tfm}.md"). Requiere "--report-azdo". + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf index 7d33561a32..b5fcb27101 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - Rédigez un résumé de travail Markdown à la fin de l’exécution du test et chargez-le via « ##vso[task.uploadsummary] ». Un argument facultatif de chemin de fichier remplace l’emplacement par défaut (« {testResultsDir}/azdo-summary-{tfm}.md »). Nécessite « --report-azdo ». - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + Rédigez un résumé de travail Markdown à la fin de l’exécution du test et chargez-le via « ##vso[task.uploadsummary] ». Un argument facultatif de chemin de fichier remplace l’emplacement par défaut (« {testResultsDir}/azdo-summary-{tfm}.md »). Nécessite « --report-azdo ». + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf index f5f83bfdef..280c7e5d32 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - Scrive un riepilogo Markdown del processo alla fine dell'esecuzione dei test e lo carica tramite "##vso[task.uploadsummary]". Un argomento facoltativo per il percorso del file sostituisce il percorso predefinito ("{testResultsDir}/azdo-summary-{tfm}.md"). Richiede "--report-azdo". - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + Scrive un riepilogo Markdown del processo alla fine dell'esecuzione dei test e lo carica tramite "##vso[task.uploadsummary]". Un argomento facoltativo per il percorso del file sostituisce il percorso predefinito ("{testResultsDir}/azdo-summary-{tfm}.md"). Richiede "--report-azdo". + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf index 4490ab4acf..118e4d7a1b 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - テスト実行の最後に Markdown のジョブ サマリーを作成し、'##vso[task.uploadsummary]' 経由でアップロードします。省略可能なファイル パス引数で、既定の場所 ('{testResultsDir}/azdo-summary-{tfm}.md') をオーバーライドできます。'--report-azdo' が必要です。 - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + テスト実行の最後に Markdown のジョブ サマリーを作成し、'##vso[task.uploadsummary]' 経由でアップロードします。省略可能なファイル パス引数で、既定の場所 ('{testResultsDir}/azdo-summary-{tfm}.md') をオーバーライドできます。'--report-azdo' が必要です。 + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf index 820b464aaa..1cf1c1d69f 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - 테스트 실행이 끝날 때 Markdown 작업 요약을 작성하고 '##vso[task.uploadsummary]'를 통해 업로드합니다. 선택적 파일 경로 인수는 기본 위치('{testResultsDir}/azdo-summary-{tfm}.md')를 재정의합니다. '--report-azdo'가 필요합니다. - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + 테스트 실행이 끝날 때 Markdown 작업 요약을 작성하고 '##vso[task.uploadsummary]'를 통해 업로드합니다. 선택적 파일 경로 인수는 기본 위치('{testResultsDir}/azdo-summary-{tfm}.md')를 재정의합니다. '--report-azdo'가 필요합니다. + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf index 8786286458..c940e1ed7c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - Zapisz podsumowanie zadania w formacie Markdown na końcu przebiegu testu i prześlij je za pomocą „##vso[task.uploadsummary]”. Opcjonalny argument ścieżki pliku zastępuje lokalizację domyślną („{testResultsDir}/azdo-summary-{tfm}.md”). Wymaga polecenia „--report-azdo”. - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + Zapisz podsumowanie zadania w formacie Markdown na końcu przebiegu testu i prześlij je za pomocą „##vso[task.uploadsummary]”. Opcjonalny argument ścieżki pliku zastępuje lokalizację domyślną („{testResultsDir}/azdo-summary-{tfm}.md”). Wymaga polecenia „--report-azdo”. + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf index c54eee9ef4..a03cb77899 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - Escreva um resumo do trabalho em Markdown no final da execução do teste e carregue-o pelo '##vso[task.uploadsummary]'. Um argumento opcional de caminho do arquivo substitui o local padrão ('{testResultsDir}/azdo-summary-{tfm}.md'). Requer '--report-azdo'. - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + Escreva um resumo do trabalho em Markdown no final da execução do teste e carregue-o pelo '##vso[task.uploadsummary]'. Um argumento opcional de caminho do arquivo substitui o local padrão ('{testResultsDir}/azdo-summary-{tfm}.md'). Requer '--report-azdo'. + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf index 3d70ab5d7b..f3422d5ecb 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - По завершении тестового запуска составьте краткий отчет в формате Markdown и загрузите его с помощью "##vso[task.uploadsummary]". Необязательный аргумент, указывающий путь к файлу, переопределяет местоположение по умолчанию ("{testResultsDir}/azdo-summary-{tfm}.md"). Требуется "--report-azdo". - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + По завершении тестового запуска составьте краткий отчет в формате Markdown и загрузите его с помощью "##vso[task.uploadsummary]". Необязательный аргумент, указывающий путь к файлу, переопределяет местоположение по умолчанию ("{testResultsDir}/azdo-summary-{tfm}.md"). Требуется "--report-azdo". + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf index dd11936cab..f74117d5b9 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - Test çalıştırmasının sonunda bir Markdown iş özeti yazın ve '##vso[task.uploadsummary]' aracılığıyla karşıya yükleyin. İsteğe bağlı bir dosya yolu bağımsız değişkeni varsayılan konumu ('{testResultsDir}/azdo-summary-{tfm}.md') geçersiz kılar. '--report-azdo' gerektirir. - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + Test çalıştırmasının sonunda bir Markdown iş özeti yazın ve '##vso[task.uploadsummary]' aracılığıyla karşıya yükleyin. İsteğe bağlı bir dosya yolu bağımsız değişkeni varsayılan konumu ('{testResultsDir}/azdo-summary-{tfm}.md') geçersiz kılar. '--report-azdo' gerektirir. + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf index 7316a9d81f..b38f36eb0a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - 在测试运行结束时编写 Markdown 作业摘要,并通过 ‘##vso[task.uploadsummary]’ 上传它。可选文件路径参数会替代默认位置('{testResultsDir}/azdo-summary-{tfm}.md')。需要 '--report-azdo'。 - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + 在测试运行结束时编写 Markdown 作业摘要,并通过 ‘##vso[task.uploadsummary]’ 上传它。可选文件路径参数会替代默认位置('{testResultsDir}/azdo-summary-{tfm}.md')。需要 '--report-azdo'。 + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf index 9683859d21..e753316130 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf @@ -343,9 +343,9 @@ {0} is the maximum pattern count. {Locked="--report-azdo-stackframe-filter"} - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. - 在測試執行結束時撰寫 Markdown 工作摘要,並透過 '##vso[task.uploadsummary]' 上傳。選用檔案路徑引數會覆寫預設位置 ('{testResultsDir}/azdo-summary-{tfm}.md')。需要 '--report-azdo'。 - {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{tfm}.md"}{Locked="--report-azdo"} + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. + 在測試執行結束時撰寫 Markdown 工作摘要,並透過 '##vso[task.uploadsummary]' 上傳。選用檔案路徑引數會覆寫預設位置 ('{testResultsDir}/azdo-summary-{tfm}.md')。需要 '--report-azdo'。 + {Locked="Markdown"}{Locked="##vso[task.uploadsummary]"}{Locked="{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md"}{Locked="--report-azdo"} Azure DevOps job summary was requested, but TF_BUILD is not set to 'true'; skipping summary emission. diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs index 4a92230154..d42c380ca6 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs @@ -146,7 +146,7 @@ Minimum number of historical samples required before a test's history is used to --report-azdo-stackframe-filter Additional regex patterns (matched against the fully-qualified type prefix of each stack frame) that should be skipped when looking for the user's call site to annotate. Repeatable; up to 16 patterns. Compiled with a 500ms match timeout. Additive to the extension's built-in MSTest assertion-implementation prefixes. --report-azdo-summary - Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. + Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. --report-azdo-upload-artifact-exclude Exclude files from Azure DevOps artifact upload using glob patterns relative to the test results directory. --report-azdo-upload-artifact-include @@ -435,7 +435,7 @@ This option takes precedence over the deprecated --no-progress flag. --report-azdo-summary Arity: 0..1 Hidden: False - Description: Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{tfm}.md'). Requires '--report-azdo'. + Description: Write a Markdown job summary at the end of the test run and upload it via '##vso[task.uploadsummary]'. An optional file path argument overrides the default location ('{testResultsDir}/azdo-summary-{assembly}-{tfm}-{arch}.md'). Requires '--report-azdo'. --report-azdo-upload-artifact-exclude Arity: 0..N Hidden: False diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsSummaryReporterTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsSummaryReporterTests.cs index c6542787bd..cf888f7772 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsSummaryReporterTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsSummaryReporterTests.cs @@ -161,6 +161,9 @@ public async Task SessionFinishing_WritesSummaryFileAndEmitsUploadSummaryCommand Assert.HasCount(1, lines); Assert.StartsWith("##vso[task.uploadsummary]", lines[0]); Assert.Contains("azdo-summary-", lines[0]); + // The assembly name must be part of the default file name so concurrent test assemblies + // sharing the same TFM and TestResults directory don't race to write the same file. + Assert.Contains("MyAssembly", lines[0]); Assert.EndsWith(".md", lines[0]); } From 6c6626f25d61c63766c3e249987476c810f6f3f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Fri, 19 Jun 2026 22:12:55 +0200 Subject: [PATCH 2/2] Sanitize the full AzDO summary file name (review feedback) Match TRX/HTML/JUnit by building the raw azdo-summary-{asm}-{tfm}-{arch}.md name and passing the whole string through ReportFileNameSanitizer, so unexpected characters in the TFM or architecture segments cannot produce an invalid name. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../AzureDevOpsSummaryReporter.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsSummaryReporter.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsSummaryReporter.cs index 8f7e5dbf57..127b9e1e41 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsSummaryReporter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsSummaryReporter.cs @@ -273,12 +273,16 @@ public async Task OnTestSessionFinishingAsync(ITestSessionContext testSessionCon // "The process cannot access the file ... because it is being used by another process". string assemblyName = _testApplicationModuleInfo.TryGetAssemblyName() ?? "unknown"; string architecture = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); - string fileName = string.Format( + + // Sanitize the whole file name (matching TRX/HTML/JUnit) so that unexpected characters in any segment + // - including the target framework moniker or architecture, not just the assembly name - cannot produce + // an invalid file name. + string fileName = ReportFileNameSanitizer.ReplaceInvalidFileNameChars(string.Format( CultureInfo.InvariantCulture, DefaultSummaryFileNameFormat, - ReportFileNameSanitizer.ReplaceInvalidFileNameChars(assemblyName), + assemblyName, _targetFrameworkMoniker.Value, - architecture); + architecture)); return Path.GetFullPath(Path.Combine(configuredTestResultsDirectory!, fileName)); }