From aa252c08574d3fd133bcc1fe14ede140efe27cba Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Wed, 15 Jan 2025 18:02:46 +0100 Subject: [PATCH] Overwrite trx when file name is explicitly provided (#4654) --- .../Resources/ExtensionResources.resx | 57 ++++++------- .../Resources/xlf/ExtensionResources.cs.xlf | 5 ++ .../Resources/xlf/ExtensionResources.de.xlf | 5 ++ .../Resources/xlf/ExtensionResources.es.xlf | 5 ++ .../Resources/xlf/ExtensionResources.fr.xlf | 5 ++ .../Resources/xlf/ExtensionResources.it.xlf | 5 ++ .../Resources/xlf/ExtensionResources.ja.xlf | 5 ++ .../Resources/xlf/ExtensionResources.ko.xlf | 5 ++ .../Resources/xlf/ExtensionResources.pl.xlf | 5 ++ .../xlf/ExtensionResources.pt-BR.xlf | 5 ++ .../Resources/xlf/ExtensionResources.ru.xlf | 5 ++ .../Resources/xlf/ExtensionResources.tr.xlf | 5 ++ .../xlf/ExtensionResources.zh-Hans.xlf | 5 ++ .../xlf/ExtensionResources.zh-Hant.xlf | 5 ++ .../TrxDataConsumer.cs | 6 +- .../TrxProcessLifetimeHandler.cs | 18 ++++- .../TrxReportEngine.cs | 31 +++++--- .../TrxReportExtensions.cs | 1 + .../TrxTests.cs | 79 ++++++++++++++----- 19 files changed, 197 insertions(+), 60 deletions(-) diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/ExtensionResources.resx b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/ExtensionResources.resx index 1fe6471fdb..d04da005ec 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/ExtensionResources.resx +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/ExtensionResources.resx @@ -1,17 +1,17 @@  - @@ -138,6 +138,9 @@ The TRX file to compare with the baseline + + Warning: Trx file '{0}' already exists and will be overwritten. + Test session diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.cs.xlf index dbbe47a6ad..f754404c01 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.cs.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.cs.xlf @@ -37,6 +37,11 @@ Soubor TRX, který se má porovnat se standardními hodnotami + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session Relace testu diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.de.xlf index f7c6142038..cd4f4c7cfc 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.de.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.de.xlf @@ -37,6 +37,11 @@ Die TRX-Datei, die mit der Baseline verglichen werden soll + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session Testsitzung diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.es.xlf index ec1263509a..4acce78fe7 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.es.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.es.xlf @@ -37,6 +37,11 @@ Archivo TRX que se va a comparar con la línea base + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session Sesión de prueba diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.fr.xlf index 7f76cb3c05..9aaf22ef40 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.fr.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.fr.xlf @@ -37,6 +37,11 @@ Fichier TRX à comparer à la ligne de base + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session Session de test diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.it.xlf index c8df530078..ce0ac7badc 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.it.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.it.xlf @@ -37,6 +37,11 @@ File TRX da confrontare con la baseline + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session Sessione di test diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ja.xlf index 0afd1ed144..fad958107f 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ja.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ja.xlf @@ -37,6 +37,11 @@ ベースラインと比較する TRX ファイル + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session テスト セッション diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ko.xlf index c818377b74..2854558598 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ko.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ko.xlf @@ -37,6 +37,11 @@ 기준과 비교할 TRX 파일입니다. + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session 테스트 세션 diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pl.xlf index e4cbf7c2cb..ef4b3067ea 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pl.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pl.xlf @@ -37,6 +37,11 @@ Plik TRX do porównania z punktem odniesienia + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session Sesja testowa diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pt-BR.xlf index 3dad1038e5..0e08d049de 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pt-BR.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pt-BR.xlf @@ -37,6 +37,11 @@ O arquivo TRX a ser comparado com a linha de base + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session Sessão de teste diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ru.xlf index 335a4f7308..5af0ada2a9 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ru.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ru.xlf @@ -37,6 +37,11 @@ TRX-файл для сравнения с базовыми показателями + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session Тестовый сеанс diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.tr.xlf index 713d10054a..f96e61fbef 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.tr.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.tr.xlf @@ -37,6 +37,11 @@ Taban çizgisiyle karşılaştırılacak TRX dosyası + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session Test oturumu diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hans.xlf index b5eab708a2..330213dacd 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hans.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hans.xlf @@ -37,6 +37,11 @@ 要与基线进行比较的 TRX 文件 + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session 测试会话 diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hant.xlf index 6bf8ca31b5..9d65d43a49 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hant.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hant.xlf @@ -37,6 +37,11 @@ 要與基準比較的 TRX 檔案 + + Warning: Trx file '{0}' already exists and will be overwritten. + Warning: Trx file '{0}' already exists and will be overwritten. + + Test session 測試工作階段 diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs index b8359cfd8d..6cf1b4627d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs @@ -237,7 +237,11 @@ public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, Cancellatio TrxReportEngine trxReportGeneratorEngine = new(_testApplicationModuleInfo, _environment, _commandLineOptionsService, _configuration, _clock, _tests.ToArray(), _failedTestsCount, _passedTestsCount, _notExecutedTestsCount, _timeoutTestsCount, _artifactsByExtension, _artifactsByTestNode, _adapterSupportTrxCapability, _testFramework, _testStartTime.Value, exitCode, cancellationToken); - string reportFileName = await trxReportGeneratorEngine.GenerateReportAsync(); + (string reportFileName, string? warning) = await trxReportGeneratorEngine.GenerateReportAsync(); + if (warning is not null) + { + await _outputDisplay.DisplayAsync(this, new WarningMessageOutputDeviceData(warning)); + } if ( // TestController is not used when we run in server mode diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs index c73914056f..935619ba20 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs @@ -7,6 +7,7 @@ using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.OutputDevice; using Microsoft.Testing.Platform.Extensions.TestFramework; using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Extensions.TestHostControllers; @@ -16,6 +17,7 @@ using Microsoft.Testing.Platform.IPC.Serializers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.OutputDevice; using Microsoft.Testing.Platform.Services; using Microsoft.Testing.Platform.TestHost; @@ -25,6 +27,7 @@ internal sealed class TrxProcessLifetimeHandler : ITestHostProcessLifetimeHandler, IDataConsumer, IDataProducer, + IOutputDeviceDataProducer, #if NETCOREAPP IAsyncDisposable #else @@ -38,6 +41,7 @@ internal sealed class TrxProcessLifetimeHandler : private readonly IConfiguration _configuration; private readonly IClock _clock; private readonly ITask _task; + private readonly IOutputDevice _outputDevice; private readonly ILogger _logger; private readonly PipeNameDescription _pipeNameDescription; private readonly Dictionary> _fileArtifacts = new(); @@ -57,6 +61,7 @@ public TrxProcessLifetimeHandler( IConfiguration configuration, IClock clock, ITask task, + IOutputDevice outputDevice, PipeNameDescription pipeNameDescription) { _commandLineOptions = commandLineOptions; @@ -66,6 +71,7 @@ public TrxProcessLifetimeHandler( _configuration = configuration; _clock = clock; _task = task; + _outputDevice = outputDevice; _pipeNameDescription = pipeNameDescription; _logger = loggerFactory.CreateLogger(); _startTime = _clock.UtcNow; @@ -165,12 +171,18 @@ public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testH testHostProcessInformation.ExitCode, cancellation); + (string fileName, string? warning) = await trxReportGeneratorEngine.GenerateReportAsync( + isTestHostCrashed: true, + testHostCrashInfo: $"Test host process pid: {testHostProcessInformation.PID} crashed."); + if (warning is not null) + { + await _outputDevice.DisplayAsync(this, new WarningMessageOutputDeviceData(warning)); + } + await _messageBus.PublishAsync( this, new FileArtifact( - new FileInfo(await trxReportGeneratorEngine.GenerateReportAsync( - isTestHostCrashed: true, - testHostCrashInfo: $"Test host process pid: {testHostProcessInformation.PID} crashed.")), + new FileInfo(fileName), ExtensionResources.TrxReportArtifactDisplayName, ExtensionResources.TrxReportArtifactDescription)); return; diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs index a359624799..9abb434a76 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs @@ -6,6 +6,7 @@ #endif using System.Security.Cryptography; +using Microsoft.Testing.Extensions.TestReports.Resources; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Extensions; @@ -142,7 +143,7 @@ public TrxReportEngine(IFileSystem fileSystem, ITestApplicationModuleInfo testAp _isCopyingFileAllowed = isCopyingFileAllowed; } - public async Task GenerateReportAsync(string testHostCrashInfo = "", bool isTestHostCrashed = false) + public async Task<(string FileName, string? Warning)> GenerateReportAsync(string testHostCrashInfo = "", bool isTestHostCrashed = false) => await RetryWhenIOExceptionAsync(async () => { string testAppModule = _testApplicationModuleInfo.GetCurrentTestApplicationFullPath(); @@ -158,9 +159,19 @@ public async Task GenerateReportAsync(string testHostCrashInfo = "", boo // If the user added the trxFileName the runDeploymentRoot would stay the same, We think it's a bug but I found that same behavior on vstest string runDeploymentRoot = AddTestSettings(testRun, testRunName); - string trxFileName = _commandLineOptionsService.TryGetOptionArgumentList(TrxReportGeneratorCommandLine.TrxReportFileNameOptionName, out string[]? fileName) - ? ReplaceInvalidFileNameChars(fileName[0]) - : $"{runDeploymentRoot}.trx"; + bool isFileNameExplicitlyProvided; + string trxFileName; + if (_commandLineOptionsService.TryGetOptionArgumentList(TrxReportGeneratorCommandLine.TrxReportFileNameOptionName, out string[]? fileName)) + { + trxFileName = ReplaceInvalidFileNameChars(fileName[0]); + isFileNameExplicitlyProvided = true; + } + else + { + trxFileName = $"{runDeploymentRoot}.trx"; + isFileNameExplicitlyProvided = false; + } + AddResults(testAppModule, testRun, out XElement testDefinitions, out XElement testEntries, out string uncategorizedTestId, out string resultSummaryOutcome); testRun.Add(testDefinitions); testRun.Add(testEntries); @@ -192,12 +203,14 @@ public async Task GenerateReportAsync(string testHostCrashInfo = "", boo // Note that we need to dispose the IFileStream, not the inner stream. // IFileStream implementations will be responsible to dispose their inner stream. - using IFileStream stream = _fileSystem.NewFileStream(finalFileName, FileMode.CreateNew); + using IFileStream stream = _fileSystem.NewFileStream(finalFileName, isFileNameExplicitlyProvided ? FileMode.Create : FileMode.CreateNew); await document.SaveAsync(stream.Stream, SaveOptions.None, _cancellationToken); - return finalFileName; + return isFileNameExplicitlyProvided && _fileSystem.Exists(finalFileName) + ? (finalFileName, string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxFileExistsAndWillBeOverwritten, finalFileName)) + : (finalFileName, null); }); - private async Task RetryWhenIOExceptionAsync(Func> func) + private async Task<(string FileName, string? Warning)> RetryWhenIOExceptionAsync(Func> func) { DateTimeOffset firstTryTime = _clock.UtcNow; bool throwIOException = false; @@ -216,8 +229,8 @@ private async Task RetryWhenIOExceptionAsync(Func> func) } } - // We try for 30 seconds to create a file with a unique name. - if (_clock.UtcNow - firstTryTime > TimeSpan.FromSeconds(30)) + // We try for 5 seconds to create a file with a unique name. + if (_clock.UtcNow - firstTryTime > TimeSpan.FromSeconds(5)) { throwIOException = true; } diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportExtensions.cs index 7a9f05c8c6..8d329aad81 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportExtensions.cs @@ -64,6 +64,7 @@ public static void AddTrxReportProvider(this ITestApplicationBuilder builder) serviceProvider.GetConfiguration(), serviceProvider.GetSystemClock(), serviceProvider.GetTask(), + serviceProvider.GetOutputDevice(), pipeNameDescription); }); ((TestHostControllersManager)builder.TestHostControllers).AddDataConsumer(compositeLifeTimeHandler); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs index da7a7862f8..5929e75def 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs @@ -39,9 +39,10 @@ public async Task TrxReportEngine_GenerateReportAsyncWithNullAdapterSupportTrxCa TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -59,9 +60,10 @@ public async Task TrxReportEngine_GenerateReportAsyncWithNotExecutedTests_TrxExe TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream, notExecutedTestsCount: 1); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -79,9 +81,10 @@ public async Task TrxReportEngine_GenerateReportAsyncWithTimeoutTests_TrxTimeout TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream, timeoutTestsCount: 1); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -98,12 +101,36 @@ public async Task TrxReportEngine_GenerateReportAsync_WithArgumentTrxReportFileN string[]? argumentTrxReportFileName = ["argumentTrxReportFileName"]; _ = _commandLineOptionsMock.Setup(_ => _.TryGetOptionArgumentList(TrxReportGeneratorCommandLine.TrxReportFileNameOptionName, out argumentTrxReportFileName)).Returns(true); PropertyBag propertyBag = new(new PassedTestNodeStateProperty()); - TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream, isExplicitFileName: true); + + // Act + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); + + // Assert + Assert.IsNull(warning); + Assert.IsTrue(fileName.Equals("argumentTrxReportFileName", StringComparison.OrdinalIgnoreCase)); + Assert.IsNotNull(memoryStream.TrxContent); + XDocument xml = memoryStream.TrxContent; + AssertTrxOutcome(xml, "Completed"); + } + + [TestMethod] + public async Task TrxReportEngine_GenerateReportAsync_WithArgumentTrxReportFileName_FileIsCorrectlyOverwrittenWhenExists() + { + // Arrange + using MemoryFileStream memoryStream = new(); + string[]? argumentTrxReportFileName = ["argumentTrxReportFileName"]; + _ = _commandLineOptionsMock.Setup(_ => _.TryGetOptionArgumentList(TrxReportGeneratorCommandLine.TrxReportFileNameOptionName, out argumentTrxReportFileName)).Returns(true); + PropertyBag propertyBag = new(new PassedTestNodeStateProperty()); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream, isExplicitFileName: true); + _ = _fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(true); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNotNull(warning); + Assert.AreEqual("Warning: Trx file 'argumentTrxReportFileName' already exists and will be overwritten.", warning); Assert.IsTrue(fileName.Equals("argumentTrxReportFileName", StringComparison.OrdinalIgnoreCase)); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -118,12 +145,13 @@ public async Task TrxReportEngine_GenerateReportAsync_WithInvalidArgumentValueFo string[]? argumentTrxReportFileName = ["NUL"]; _ = _commandLineOptionsMock.Setup(_ => _.TryGetOptionArgumentList(TrxReportGeneratorCommandLine.TrxReportFileNameOptionName, out argumentTrxReportFileName)).Returns(true); PropertyBag propertyBag = new(new PassedTestNodeStateProperty()); - TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream, isExplicitFileName: true); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); Assert.IsTrue(fileName.Equals("_NUL", StringComparison.OrdinalIgnoreCase)); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -139,9 +167,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithTestHostCrash_ResultSu TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream); // Act - string fileName = await trxReportEngine.GenerateReportAsync(isTestHostCrashed: true); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(isTestHostCrashed: true); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -157,9 +186,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithTestSkipped_ResultSumm TrxReportEngine trxReportEngine = GenerateTrxReportEngine(0, 0, propertyBag, memoryStream); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -177,9 +207,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithTestFailed_WithStandar TrxReportEngine trxReportEngine = GenerateTrxReportEngine(0, 1, propertyBag, memoryStream); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -211,9 +242,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithTestFailed_WithoutStan TrxReportEngine trxReportEngine = GenerateTrxReportEngine(0, 1, propertyBag, memoryStream); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -240,9 +272,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithTestFailed_WithoutStan TrxReportEngine trxReportEngine = GenerateTrxReportEngine(0, 1, propertyBag, memoryStream); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -272,9 +305,10 @@ public async Task TrxReportEngine_GenerateReportAsync_PassedTestWithTestCategory TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -300,9 +334,10 @@ public async Task TrxReportEngine_GenerateReportAsync_FailedTestWithTestCategory TrxReportEngine trxReportEngine = GenerateTrxReportEngine(0, 1, propertyBag, memoryStream); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -329,9 +364,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithAdapterSupportTrxCapab propertyBag, memoryStream, true); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -350,9 +386,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithArtifactsByTestNode_Tr new(new PassedTestNodeStateProperty()), memoryStream); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -380,9 +417,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithArtifactsByExtension_T new(new PassedTestNodeStateProperty()), memoryStream); // Act - string fileName = await trxReportEngine.GenerateReportAsync(); + (string fileName, string? warning) = await trxReportEngine.GenerateReportAsync(); // Assert + Assert.IsNull(warning); AssertExpectedTrxFileName(fileName); Assert.IsNotNull(memoryStream.TrxContent); XDocument xml = memoryStream.TrxContent; @@ -450,7 +488,8 @@ private static void AssertExpectedTrxFileName(string fileName) => Assert.IsTrue(fileName.Equals("_MachineName_0001-01-01_00_00_00.000.trx", StringComparison.Ordinal)); private TrxReportEngine GenerateTrxReportEngine(int passedTestsCount, int failedTestsCount, PropertyBag propertyBag, MemoryFileStream memoryStream, - bool? adapterSupportTrxCapability = null, int notExecutedTestsCount = 0, int timeoutTestsCount = 0) + bool? adapterSupportTrxCapability = null, int notExecutedTestsCount = 0, int timeoutTestsCount = 0, + bool isExplicitFileName = false) { var testNode = new TestNodeUpdateMessage( new SessionUid("1"), @@ -461,8 +500,8 @@ private TrxReportEngine GenerateTrxReportEngine(int passedTestsCount, int failed DateTime testStartTime = DateTime.Now; CancellationToken cancellationToken = CancellationToken.None; - _ = _fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(true); - _ = _fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.CreateNew)) + _ = _fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(false); + _ = _fileSystem.Setup(x => x.NewFileStream(It.IsAny(), isExplicitFileName ? FileMode.Create : FileMode.CreateNew)) .Returns(memoryStream); _ = _configurationMock.SetupGet(_ => _[It.IsAny()]).Returns(string.Empty);