diff --git a/src/Analyzers/ITestResultsAnalyzer.cs b/src/Analyzers/ITestResultsAnalyzer.cs index cbccb3d..a661055 100644 --- a/src/Analyzers/ITestResultsAnalyzer.cs +++ b/src/Analyzers/ITestResultsAnalyzer.cs @@ -4,11 +4,11 @@ namespace dotnet.test.rerun.Analyzers; public interface ITestResultsAnalyzer { - string GetFailedTestsFilter(IFileInfo trxFile); + string GetFailedTestsFilter(IFileInfo[] trxFiles); - IFileInfo? GetTrxFile(IDirectoryInfo resultsDirectory); + IFileInfo[] GetTrxFiles(IDirectoryInfo resultsDirectory, DateTime startSearchTime); - void AddLastTrxFile(IDirectoryInfo resultsDirectory); + void AddLastTrxFiles(IDirectoryInfo resultsDirectory, DateTime startSearchTime); HashSet GetReportFiles(); } \ No newline at end of file diff --git a/src/Analyzers/TestResultsAnalyzer.cs b/src/Analyzers/TestResultsAnalyzer.cs index 251c63b..7dc871e 100644 --- a/src/Analyzers/TestResultsAnalyzer.cs +++ b/src/Analyzers/TestResultsAnalyzer.cs @@ -14,43 +14,47 @@ public TestResultsAnalyzer(ILogger logger) Log = logger; reportFiles = new (); } - - public string GetFailedTestsFilter(IFileInfo trxFile) + + public string GetFailedTestsFilter(IFileInfo[] trxFiles) { - const string outcome = "Failed"; - var trx = TrxDeserializer.Deserialize(trxFile.FullName); - reportFiles.Add(trxFile.FullName); + var failedTests = new List(); + foreach (var trxFile in trxFiles) + failedTests.AddRange(GetFailedTestsFilter(trxFile)); - var tests = trx.Results.UnitTestResults - .Where(t => t.Outcome.Equals(outcome, StringComparison.InvariantCultureIgnoreCase)) - .Select(t => $"FullyQualifiedName~{(t.TestName.IndexOf("(") > 0 ? t.TestName.Substring(0, t.TestName.IndexOf("(")) : t.TestName).TrimEnd()}") - .Distinct() - .ToList(); - - if (tests.Count == 0) - { - Log.Warning($"No tests found with the Outcome {outcome}"); - return ""; - } - - var testFilter = string.Join(" | ", tests); + var testFilter = string.Join(" | ", failedTests); Log.Debug(testFilter); return testFilter; } - public IFileInfo? GetTrxFile(IDirectoryInfo resultsDirectory) + public IFileInfo[] GetTrxFiles(IDirectoryInfo resultsDirectory, DateTime startSearchTime) => resultsDirectory.Exists ? - resultsDirectory.EnumerateFiles("*.trx").MaxBy(f => f.Name) : - default; + resultsDirectory.EnumerateFiles("*.trx").Where(file => file.CreationTime >= startSearchTime).ToArray() : + Array.Empty(); - public void AddLastTrxFile(IDirectoryInfo resultsDirectory) + public void AddLastTrxFiles(IDirectoryInfo resultsDirectory, DateTime startSearchTime) { - var fileInfo = GetTrxFile(resultsDirectory); - - if (fileInfo is not null) + foreach (var fileInfo in GetTrxFiles(resultsDirectory, startSearchTime)) reportFiles.Add(fileInfo.FullName); } public HashSet GetReportFiles() => reportFiles; + + private List GetFailedTestsFilter(IFileInfo trxFile) + { + const string outcome = "Failed"; + var trx = TrxDeserializer.Deserialize(trxFile.FullName); + reportFiles.Add(trxFile.FullName); + + var tests = trx.Results?.UnitTestResults + .Where(t => t.Outcome.Equals(outcome, StringComparison.InvariantCultureIgnoreCase)) + .Select(t => $"FullyQualifiedName~{(t.TestName.IndexOf("(") > 0 ? t.TestName.Substring(0, t.TestName.IndexOf("(")) : t.TestName).TrimEnd()}") + .Distinct() + .ToList() ?? new List(); + + if (tests.Count == 0) + Log.Warning($"No tests found with the Outcome {outcome} in file {trxFile.Name}"); + + return tests; + } } \ No newline at end of file diff --git a/src/RerunCommand/RerunCommand.cs b/src/RerunCommand/RerunCommand.cs index 51c22f4..7552a9e 100644 --- a/src/RerunCommand/RerunCommand.cs +++ b/src/RerunCommand/RerunCommand.cs @@ -45,8 +45,8 @@ public RerunCommand(ILogger logger, public async Task Run() { var startOfProcess = DateTime.Now; + var startOfDotnetRun = DateTime.Now; IDirectoryInfo resultsDirectory = FileSystem.DirectoryInfo.New(Config.ResultsDirectory); - var oldTrxFile = TestResultsAnalyzer.GetTrxFile(resultsDirectory); await DotNetTestRunner.Test(Config, resultsDirectory.FullName); if (DotNetTestRunner.GetErrorCode() == ErrorCode.FailedTests) { @@ -54,19 +54,11 @@ public async Task Run() while (attempt <= Config.RerunMaxAttempts) { await Task.Delay(Config.Delay); - var trxFile = TestResultsAnalyzer.GetTrxFile(resultsDirectory); + var trxFiles = TestResultsAnalyzer.GetTrxFiles(resultsDirectory, startOfDotnetRun); - if (trxFile != null) + if (trxFiles.Length > 0) { - if (oldTrxFile != null && - trxFile.FullName.Equals(oldTrxFile.FullName, StringComparison.InvariantCultureIgnoreCase)) - { - Log.Error("No new trx file was generated"); - break; - } - - var testsToRerun = TestResultsAnalyzer.GetFailedTestsFilter(trxFile); - oldTrxFile = trxFile; + var testsToRerun = TestResultsAnalyzer.GetFailedTestsFilter(trxFiles); if (string.IsNullOrEmpty(testsToRerun)) { Environment.ExitCode = 0; @@ -77,6 +69,7 @@ public async Task Run() Log.Information($"Rerun attempt {attempt}/{Config.RerunMaxAttempts}"); Log.Warning($"Found Failed tests. Rerun filter: {testsToRerun}"); Config.Filter = testsToRerun; + startOfDotnetRun = DateTime.Now; await DotNetTestRunner.Test(Config, resultsDirectory.FullName); attempt++; } @@ -95,7 +88,7 @@ public async Task Run() if (Config.DeleteReportFiles) { - TestResultsAnalyzer.AddLastTrxFile(resultsDirectory); + TestResultsAnalyzer.AddLastTrxFiles(resultsDirectory, startOfDotnetRun); DeleteFiles(); } diff --git a/test/dotnet-test-rerun.Common/Utilities/TestUtilities.cs b/test/dotnet-test-rerun.Common/Utilities/TestUtilities.cs index 7d8134d..5889f36 100644 --- a/test/dotnet-test-rerun.Common/Utilities/TestUtilities.cs +++ b/test/dotnet-test-rerun.Common/Utilities/TestUtilities.cs @@ -32,6 +32,23 @@ public static void CopyFixture(string fixtureName, DirectoryInfo target) CopyAll(source, target); } + /// + /// Copies the fixture to a directory + /// + /// + /// + /// + public static void CopyFixtureFile(string folder, string fixtureName, DirectoryInfo target) + { + Directory.CreateDirectory(target.FullName); + var source = new DirectoryInfo(System.IO.Path.GetFullPath( + System.IO.Path.Join( + AppDomain.CurrentDomain.BaseDirectory, + "..", "..", "..", "Fixtures", folder))); + var file = source.GetFiles().FirstOrDefault(file => file.Name == fixtureName); + file!.CopyTo(Path.Combine(target.FullName, file.Name), true); + } + /// /// Copies all files from a source directory to a target directory /// diff --git a/test/dotnet-test-rerun.IntegrationTests/DotNetTestRerunTests.cs b/test/dotnet-test-rerun.IntegrationTests/DotNetTestRerunTests.cs index 2b75f4d..498ff58 100644 --- a/test/dotnet-test-rerun.IntegrationTests/DotNetTestRerunTests.cs +++ b/test/dotnet-test-rerun.IntegrationTests/DotNetTestRerunTests.cs @@ -10,7 +10,6 @@ using dotnet.test.rerun.RerunCommand; using FluentAssertions; using Xunit.Abstractions; -using Xunit.Sdk; namespace dotnet_test_rerun.IntegrationTests; @@ -267,6 +266,29 @@ public async Task DotnetTestRerun_FailingXUnit_WithDeleteFiles() var files = FileSystem.Directory.EnumerateFiles(testDir, "*trx"); files.Should().HaveCount(0); } + + [Fact] + public async Task DotnetTestRerun_FailingNUnit_PassOnSecond_TwoFailingProjects() + { + // Arrange + Environment.ExitCode = 0; + + // Act + var output = await RunDotNetTestRerunAndCollectOutputMessage("NUnitMultipleFailingProjects"); + + // Assert + output.Should().Contain("Passed!"); + output.Should().Contain("Failed!", Exactly.Times(3)); + output.Should().Contain("Rerun filter:", + Exactly.Twice()); + output.Should().Contain("Failed: 2, Passed: 1", + Exactly.Once()); + output.Should().Contain("Failed: 1, Passed: 1", + Exactly.Twice()); + output.Should().Contain("Failed: 0, Passed: 1", + Exactly.Twice()); + Environment.ExitCode.Should().Be(0); + } private async Task RunDotNetTestRerunAndCollectOutputMessage(string proj, string extraArgs = "", string? dir = null) { diff --git a/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitMultipleFailingProjects.sln b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitMultipleFailingProjects.sln new file mode 100644 index 0000000..1c76231 --- /dev/null +++ b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitMultipleFailingProjects.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NUnitTestPassOnSecondRunFirstExample", "NUnitTestPassOnSecondRunFirstExample\NUnitTestPassOnSecondRunFirstExample.csproj", "{05625348-7376-4E0C-98EE-AEC93C1EA85B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NUnitTestPassOnThirdRunSecondExample", "NUnitTestPassOnThirdRunSecondExample\NUnitTestPassOnThirdRunSecondExample.csproj", "{F65E419F-ED1C-49F1-A32F-CBD95FFCB0B8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {05625348-7376-4E0C-98EE-AEC93C1EA85B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05625348-7376-4E0C-98EE-AEC93C1EA85B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05625348-7376-4E0C-98EE-AEC93C1EA85B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05625348-7376-4E0C-98EE-AEC93C1EA85B}.Release|Any CPU.Build.0 = Release|Any CPU + {F65E419F-ED1C-49F1-A32F-CBD95FFCB0B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F65E419F-ED1C-49F1-A32F-CBD95FFCB0B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F65E419F-ED1C-49F1-A32F-CBD95FFCB0B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F65E419F-ED1C-49F1-A32F-CBD95FFCB0B8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnSecondRunFirstExample/NUnitTestPassOnSecondRunFirstExample.csproj b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnSecondRunFirstExample/NUnitTestPassOnSecondRunFirstExample.csproj new file mode 100644 index 0000000..6d81cfe --- /dev/null +++ b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnSecondRunFirstExample/NUnitTestPassOnSecondRunFirstExample.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + enable + enable + + false + + + + + + + + + + + diff --git a/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnSecondRunFirstExample/SimpleTest.cs b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnSecondRunFirstExample/SimpleTest.cs new file mode 100644 index 0000000..a8b1736 --- /dev/null +++ b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnSecondRunFirstExample/SimpleTest.cs @@ -0,0 +1,20 @@ +namespace NUnitTestPassOnSecondRunFirstExample; + + +public class Tests +{ + private int number = 2; + + [Test, Order(1)] + public void NUnitTestPassOnSecondRunFirstExample_FirstNumberCompare() + { + number = 3; + Assert.AreEqual(3, number); + } + + [Test, Order(2)] + public void NUnitTestPassOnSecondRunFirstExample_SecondNumberCompare() + { + Assert.AreEqual(2, number); + } +} \ No newline at end of file diff --git a/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnSecondRunFirstExample/Usings.cs b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnSecondRunFirstExample/Usings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnSecondRunFirstExample/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnThirdRunSecondExample/NUnitTestPassOnThirdRunSecondExample.csproj b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnThirdRunSecondExample/NUnitTestPassOnThirdRunSecondExample.csproj new file mode 100644 index 0000000..d97d5f5 --- /dev/null +++ b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnThirdRunSecondExample/NUnitTestPassOnThirdRunSecondExample.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + enable + + false + + NUnitTestPassOnSecondRunSecondExample + + + + + + + + + + + diff --git a/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnThirdRunSecondExample/SimpleTest.cs b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnThirdRunSecondExample/SimpleTest.cs new file mode 100644 index 0000000..b02c2a8 --- /dev/null +++ b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnThirdRunSecondExample/SimpleTest.cs @@ -0,0 +1,29 @@ +namespace NUnitTestPassOnSecondRunSecondExample; + + +public class Tests +{ + private int firstNumber = 2; + private int secondNumber = 2; + + [Test, Order(1)] + public void NUnitTestPassOnThirdRunSecondExample_FirstNumberCompare() + { + firstNumber = 3; + Assert.AreEqual(3, firstNumber); + } + + [Test, Order(2)] + public void NUnitTestPassOnThirdRunSecondExample_SecondSimpleNumberCompare() + { + secondNumber = 4; + Assert.AreEqual(2, firstNumber); + Assert.AreEqual(4, secondNumber); + } + + [Test, Order(3)] + public void NUnitTestPassOnThirdRunSecondExample_ThirdSimpleNumberCompare() + { + Assert.AreEqual(2, secondNumber); + } +} \ No newline at end of file diff --git a/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnThirdRunSecondExample/Usings.cs b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnThirdRunSecondExample/Usings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/test/dotnet-test-rerun.IntegrationTests/Fixtures/NUnitMultipleFailingProjects/NUnitTestPassOnThirdRunSecondExample/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/test/dotnet-test-rerun.UnitTests/Analyzers/TestResultsAnalyzerTests.cs b/test/dotnet-test-rerun.UnitTests/Analyzers/TestResultsAnalyzerTests.cs index 0ff00cf..97a2859 100644 --- a/test/dotnet-test-rerun.UnitTests/Analyzers/TestResultsAnalyzerTests.cs +++ b/test/dotnet-test-rerun.UnitTests/Analyzers/TestResultsAnalyzerTests.cs @@ -1,7 +1,7 @@ using System.IO.Abstractions; +using dotnet_test_rerun.IntegrationTests.Utilities; using dotnet.test.rerun.Analyzers; using dotnet.test.rerun.Logging; -using dotnet.test.rerun.RerunCommand; using FluentAssertions; using Xunit; @@ -12,7 +12,7 @@ public class TestResultsAnalyzerTests private static readonly IFileSystem FileSystem = new FileSystem(); private static readonly ILogger Logger = new Logger(); - private TestResultsAnalyzer TestResultsAnalyzer = new TestResultsAnalyzer(Logger); + private TestResultsAnalyzer TestResultsAnalyzer = new (Logger); private readonly IDirectoryInfo ResultsDirectory = FileSystem.DirectoryInfo.New("../../../Fixtures/RerunCommand/"); [Fact] @@ -22,7 +22,7 @@ public void GetFailedTestsFilter_XUnit_NoFailedTests_ReturnEmpty() var trxFile = ResultsDirectory.EnumerateFiles("XUnitTrxFileWithAllTestsPassing.trx").OrderBy(f => f.Name).LastOrDefault(); //Act - var result = TestResultsAnalyzer.GetFailedTestsFilter(trxFile!); + var result = TestResultsAnalyzer.GetFailedTestsFilter(new[] { trxFile!}); //Assert result.Should().BeEmpty(); @@ -35,7 +35,7 @@ public void GetFailedTestsFilter_XUnit_OneFailedTest_ReturnOne() var trxFile = ResultsDirectory.EnumerateFiles("XUnitTrxFileWithOneFailedTest.trx").OrderBy(f => f.Name).LastOrDefault(); //Act - var result = TestResultsAnalyzer.GetFailedTestsFilter(trxFile!); + var result = TestResultsAnalyzer.GetFailedTestsFilter(new[] { trxFile!}); //Assert result.Should().Be("FullyQualifiedName~XUnitExample.SimpleTest.SimpleStringCompare"); @@ -48,7 +48,7 @@ public void GetFailedTestsFilter_XUnit_SeveralFailedTest_ReturnSeveral() var trxFile = ResultsDirectory.EnumerateFiles("XUnitTrxFileWithSeveralFailedTests.trx").OrderBy(f => f.Name).LastOrDefault(); //Act - var result = TestResultsAnalyzer.GetFailedTestsFilter(trxFile!); + var result = TestResultsAnalyzer.GetFailedTestsFilter(new[] { trxFile!}); //Assert result.Should().Be("FullyQualifiedName~XUnitExample.UnitTest1.SimpleNumberCompare | FullyQualifiedName~XUnitExample.UnitTest1.SimpleStringCompare"); @@ -61,7 +61,7 @@ public void GetFailedTestsFilter_NUnit_NoFailedTests_ReturnEmpty() var trxFile = ResultsDirectory.EnumerateFiles("NUnitTrxFileWithAllTestsPassing.trx").OrderBy(f => f.Name).LastOrDefault(); //Act - var result = TestResultsAnalyzer.GetFailedTestsFilter(trxFile!); + var result = TestResultsAnalyzer.GetFailedTestsFilter(new[] { trxFile!}); //Assert result.Should().BeEmpty(); @@ -74,7 +74,7 @@ public void GetFailedTestsFilter_NUnit_OneFailedTest_ReturnOne() var trxFile = ResultsDirectory.EnumerateFiles("NUnitTrxFileWithOneFailedTest.trx").OrderBy(f => f.Name).LastOrDefault(); //Act - var result = TestResultsAnalyzer.GetFailedTestsFilter(trxFile!); + var result = TestResultsAnalyzer.GetFailedTestsFilter(new[] { trxFile!}); //Assert result.Should().Be("FullyQualifiedName~SimpleStringCompare"); @@ -87,7 +87,7 @@ public void GetFailedTestsFilter_NUnit_SeveralFailedTest_ReturnSeveral() var trxFile = ResultsDirectory.EnumerateFiles("NUnitTrxFileWithSeveralFailedTests.trx").OrderBy(f => f.Name).LastOrDefault(); //Act - var result = TestResultsAnalyzer.GetFailedTestsFilter(trxFile!); + var result = TestResultsAnalyzer.GetFailedTestsFilter(new[] { trxFile!}); //Assert result.Should().Be("FullyQualifiedName~SimpleStringCompare | FullyQualifiedName~SimpleNumberCompare"); @@ -100,7 +100,7 @@ public void GetFailedTestsFilter_MsTest_NoFailedTests_ReturnEmpty() var trxFile = ResultsDirectory.EnumerateFiles("MsTestTrxFileWithAllTestsPassing.trx").OrderBy(f => f.Name).LastOrDefault(); //Act - var result = TestResultsAnalyzer.GetFailedTestsFilter(trxFile!); + var result = TestResultsAnalyzer.GetFailedTestsFilter(new[] { trxFile!}); //Assert result.Should().BeEmpty(); @@ -113,7 +113,7 @@ public void GetFailedTestsFilter_MsTest_OneFailedTest_ReturnOne() var trxFile = ResultsDirectory.EnumerateFiles("MsTestTrxFileWithOneFailedTest.trx").OrderBy(f => f.Name).LastOrDefault(); //Act - var result = TestResultsAnalyzer.GetFailedTestsFilter(trxFile!); + var result = TestResultsAnalyzer.GetFailedTestsFilter(new[] { trxFile!}); //Assert result.Should().Be("FullyQualifiedName~SimpleStringCompare"); @@ -126,46 +126,115 @@ public void GetFailedTestsFilter_MsTest_SeveralFailedTest_ReturnSeveral() var trxFile = ResultsDirectory.EnumerateFiles("MsTestTrxFileWithSeveralFailedTests.trx").OrderBy(f => f.Name).LastOrDefault(); //Act - var result = TestResultsAnalyzer.GetFailedTestsFilter(trxFile!); + var result = TestResultsAnalyzer.GetFailedTestsFilter(new[] { trxFile!}); //Assert result.Should().Be("FullyQualifiedName~SimpleNumberCompare | FullyQualifiedName~SimpleStringCompare"); } [Fact] - public void GetTrxFile_FromValidDirectory_ReturnFile() + public void GetTrxFiles_FromValidDirectory_ReturnFile() + { + //Arrange + DateTime start = DateTime.MinValue; + var testDirPath = TestUtilities.GetTmpDirectory(); + IDirectoryInfo testDir = FileSystem.DirectoryInfo.New(testDirPath); + TestUtilities.CopyFixtureFile("RerunCommand", "XUnitTrxFileWithSeveralFailedTests.trx", new DirectoryInfo(testDirPath)); + + //Act + var result = TestResultsAnalyzer.GetTrxFiles(testDir, start); + + //Assert + result.Should().NotBeNull(); + result.Should().HaveCount(1); + result[0].Name.Should().Be("XUnitTrxFileWithSeveralFailedTests.trx"); + } + + + [Fact] + public void GetTrxFiles_FromValidDirectory_ReturnFiles() { //Act - var result = TestResultsAnalyzer.GetTrxFile(ResultsDirectory); + var result = TestResultsAnalyzer.GetTrxFiles(ResultsDirectory, DateTime.MinValue); //Assert result.Should().NotBeNull(); - result!.Name.Should().Be("XUnitTrxFileWithSeveralFailedTests.trx"); + result.Should().HaveCount(9); } [Fact] - public void GetTrxFile_FromDirectoryWithoutFiles_ReturnNoFile() + public void GetTrxFiles_FromDirectoryWithoutFiles_ReturnNoFile() { //Arrange IDirectoryInfo dir = FileSystem.DirectoryInfo.New("."); + DateTime start = DateTime.MinValue; //Act - var result = TestResultsAnalyzer.GetTrxFile(dir); + var result = TestResultsAnalyzer.GetTrxFiles(dir, start); //Assert - result.Should().BeNull(); + result.Should().BeEmpty(); } [Fact] public void AddLastTrxFile_FromValidDirectory_ReturnFiles() { + //Arrange + DateTime start = DateTime.MinValue; + var testDirPath = TestUtilities.GetTmpDirectory(); + IDirectoryInfo testDir = FileSystem.DirectoryInfo.New(testDirPath); + TestUtilities.CopyFixtureFile("RerunCommand", "MsTestTrxFileWithOneFailedTest.trx", new DirectoryInfo(testDirPath)); + //Act - TestResultsAnalyzer.AddLastTrxFile(ResultsDirectory); + TestResultsAnalyzer.AddLastTrxFiles(testDir, start); //Assert var reportFiles = TestResultsAnalyzer.GetReportFiles(); reportFiles.Should().NotBeNull(); reportFiles.Should().HaveCount(1); - reportFiles.ElementAt(0).Should().Be(TestResultsAnalyzer.GetTrxFile(ResultsDirectory)?.FullName); + var trxFiles = TestResultsAnalyzer.GetTrxFiles(testDir, start); + trxFiles.Should().HaveCount(1); + reportFiles.ElementAt(0).Should().Be(trxFiles[0].FullName); + } + + [Fact] + public void GetFailedTestsFilter_MsTest_NoFailedTests_WithTwoFiles_ReturnEmpty() + { + //Arrange + var trxFile = ResultsDirectory.EnumerateFiles("MsTestTrxFileWithAllTestsPassing.trx").OrderBy(f => f.Name).LastOrDefault(); + + //Act + var result = TestResultsAnalyzer.GetFailedTestsFilter(new[] { trxFile!, trxFile!}); + + //Assert + result.Should().BeEmpty(); + } + + [Fact] + public void GetFailedTestsFilter_NUnit_FailedTests_InTwoFiles_ReturnAll() + { + //Arrange + var firstTrxFile = ResultsDirectory.EnumerateFiles("NUnitTrxFileWithOneFailedTest.trx").OrderBy(f => f.Name).LastOrDefault(); + var secondTrxFile = ResultsDirectory.EnumerateFiles("NUnitTrxFileWithSeveralFailedTests.trx").OrderBy(f => f.Name).LastOrDefault(); + + //Act + var result = TestResultsAnalyzer.GetFailedTestsFilter(new[] { firstTrxFile!, secondTrxFile!}); + + //Assert + result.Should().Be("FullyQualifiedName~SimpleStringCompare | FullyQualifiedName~SimpleStringCompare | FullyQualifiedName~SimpleNumberCompare"); + } + + [Fact] + public void GetFailedTestsFilter_NUnit_OneFileWithFailing_AnotherWithPassing_ReturnOne() + { + //Arrange + var firstTrxFile = ResultsDirectory.EnumerateFiles("NUnitTrxFileWithOneFailedTest.trx").OrderBy(f => f.Name).LastOrDefault(); + var secondTrxFile = ResultsDirectory.EnumerateFiles("NUnitTrxFileWithAllTestsPassing.trx").OrderBy(f => f.Name).LastOrDefault(); + + //Act + var result = TestResultsAnalyzer.GetFailedTestsFilter(new[] { firstTrxFile!, secondTrxFile!}); + + //Assert + result.Should().Be("FullyQualifiedName~SimpleStringCompare"); } } \ No newline at end of file diff --git a/test/dotnet-test-rerun.UnitTests/RerunCommand/RerunCommandTests.cs b/test/dotnet-test-rerun.UnitTests/RerunCommand/RerunCommandTests.cs index e38deb6..c669ca9 100644 --- a/test/dotnet-test-rerun.UnitTests/RerunCommand/RerunCommandTests.cs +++ b/test/dotnet-test-rerun.UnitTests/RerunCommand/RerunCommandTests.cs @@ -87,20 +87,19 @@ public async Task Run_TestsFail_NoTrxFound() .Returns(Task.CompletedTask); dotNetTestRunner.Setup(x => x.GetErrorCode()) .Returns(ErrorCode.FailedTests); - testResultsAnalyzer.SetupSequence(x => x.GetTrxFile(It.IsAny())) - .Returns((IFileInfo?)null) - .Returns((IFileInfo?)null); + testResultsAnalyzer.SetupSequence(x => x.GetTrxFiles(It.IsAny(), It.IsAny())) + .Returns(Array.Empty()); // Act await command.Run(); // Assert dotNetTestRunner.Verify(x => x.Test(config, directoryInfo.FullName), Times.Once); - testResultsAnalyzer.Verify(x => x.GetTrxFile(It.IsAny()), Times.Exactly(2)); + testResultsAnalyzer.Verify(x => x.GetTrxFiles(It.IsAny(), It.IsAny()), Times.Once); } [Fact] - public async Task Run_TestsFail_NoNewTrxFound() + public async Task Run_TestsFail_NoTestsFoundInTrxToRerun() { // Arrange var logger = new Logger(); @@ -113,26 +112,27 @@ public async Task Run_TestsFail_NoNewTrxFound() var directoryInfo = fileSystem.DirectoryInfo.New(config.ResultsDirectory); var command = new dotnet.test.rerun.RerunCommand.RerunCommand(logger, config, dotNetTestRunner.Object, dotNetCoverageRunner.Object, fileSystem, testResultsAnalyzer.Object); - var initialTrxFile = fileSystem.FileInfo.New("Initial.trx"); + var trxFile = fileSystem.FileInfo.New("Second.trx"); dotNetTestRunner.Setup(x => x.Test(config, directoryInfo.FullName)) .Returns(Task.CompletedTask); dotNetTestRunner.Setup(x => x.GetErrorCode()) .Returns(ErrorCode.FailedTests); - testResultsAnalyzer.SetupSequence(x => x.GetTrxFile(It.IsAny())) - .Returns(initialTrxFile) - .Returns(initialTrxFile); + testResultsAnalyzer.SetupSequence(x => x.GetTrxFiles(It.IsAny(), It.IsAny())) + .Returns(new[] { trxFile }); + testResultsAnalyzer.Setup(x => x.GetFailedTestsFilter(It.Is(files => files[0] == trxFile))) + .Returns(String.Empty); // Act await command.Run(); // Assert dotNetTestRunner.Verify(x => x.Test(config, directoryInfo.FullName), Times.Once); - testResultsAnalyzer.Verify(x => x.GetTrxFile(It.IsAny()), Times.Exactly(2)); + testResultsAnalyzer.Verify(x => x.GetTrxFiles(It.IsAny(), It.IsAny()), Times.Once); } [Fact] - public async Task Run_TestsFail_NoTestsFoundInTrxToRerun() + public async Task Run_TestsFailOnFirstRun_PassOnSecond() { // Arrange var logger = new Logger(); @@ -148,26 +148,31 @@ public async Task Run_TestsFail_NoTestsFoundInTrxToRerun() var firstTrxFile = fileSystem.FileInfo.New("First.trx"); var secondTrxFile = fileSystem.FileInfo.New("Second.trx"); - dotNetTestRunner.Setup(x => x.Test(config, directoryInfo.FullName)) + dotNetTestRunner.SetupSequence(x => x.Test(config, directoryInfo.FullName)) + .Returns(Task.CompletedTask) .Returns(Task.CompletedTask); - dotNetTestRunner.Setup(x => x.GetErrorCode()) - .Returns(ErrorCode.FailedTests); - testResultsAnalyzer.SetupSequence(x => x.GetTrxFile(It.IsAny())) - .Returns(firstTrxFile) - .Returns(secondTrxFile); - testResultsAnalyzer.Setup(x => x.GetFailedTestsFilter(secondTrxFile)) - .Returns(String.Empty); + dotNetTestRunner.SetupSequence(x => x.GetErrorCode()) + .Returns(ErrorCode.FailedTests) + .Returns(ErrorCode.Success); + testResultsAnalyzer.SetupSequence(x => x.GetTrxFiles(It.IsAny(), It.IsAny())) + .Returns(new[] { firstTrxFile }) + .Returns(new[] { secondTrxFile }); + testResultsAnalyzer.Setup(x => x.GetFailedTestsFilter(It.Is(files => files[0] == firstTrxFile))) + .Returns("filterToRerun"); + testResultsAnalyzer.Setup(x => x.GetFailedTestsFilter(It.Is(files => files[0] == secondTrxFile))) + .Returns(string.Empty); // Act await command.Run(); // Assert - dotNetTestRunner.Verify(x => x.Test(config, directoryInfo.FullName), Times.Once); - testResultsAnalyzer.Verify(x => x.GetTrxFile(It.IsAny()), Times.Exactly(2)); + dotNetTestRunner.Verify(x => x.Test(config, directoryInfo.FullName), Times.Exactly(2)); + testResultsAnalyzer.Verify(x => x.GetTrxFiles(It.IsAny(), It.IsAny()), Times.Exactly(2)); + testResultsAnalyzer.Verify(x => x.GetFailedTestsFilter(new[] { secondTrxFile}), Times.Exactly(1)); } [Fact] - public async Task Run_TestsFailOnFirstRun_PassOnSecond() + public async Task Run_TestsFailOnFirstRunWithMultipleTrxFiles_PassOnSecond() { // Arrange var logger = new Logger(); @@ -189,11 +194,10 @@ public async Task Run_TestsFailOnFirstRun_PassOnSecond() dotNetTestRunner.SetupSequence(x => x.GetErrorCode()) .Returns(ErrorCode.FailedTests) .Returns(ErrorCode.Success); - testResultsAnalyzer.SetupSequence(x => x.GetTrxFile(It.IsAny())) - .Returns(firstTrxFile) - .Returns(secondTrxFile) - .Returns(secondTrxFile); - testResultsAnalyzer.Setup(x => x.GetFailedTestsFilter(secondTrxFile)) + testResultsAnalyzer.SetupSequence(x => x.GetTrxFiles(It.IsAny(), It.IsAny())) + .Returns(new[] { firstTrxFile, secondTrxFile }) + .Returns(Array.Empty() ); + testResultsAnalyzer.Setup(x => x.GetFailedTestsFilter(It.Is(files => files[0] == firstTrxFile && files[1] == secondTrxFile))) .Returns("filterToRerun"); // Act @@ -201,9 +205,10 @@ public async Task Run_TestsFailOnFirstRun_PassOnSecond() // Assert dotNetTestRunner.Verify(x => x.Test(config, directoryInfo.FullName), Times.Exactly(2)); - testResultsAnalyzer.Verify(x => x.GetTrxFile(It.IsAny()), Times.Exactly(3)); - testResultsAnalyzer.Verify(x => x.GetFailedTestsFilter(secondTrxFile), Times.Exactly(1)); + testResultsAnalyzer.Verify(x => x.GetTrxFiles(It.IsAny(), It.IsAny()), Times.Exactly(2)); + testResultsAnalyzer.Verify(x => x.GetFailedTestsFilter(new[] { firstTrxFile, secondTrxFile}), Times.Once); } + [Fact] public async Task Run_TestsFailOnFirstRun_PassOnSecond_WithDeleteReports() { @@ -227,12 +232,13 @@ public async Task Run_TestsFailOnFirstRun_PassOnSecond_WithDeleteReports() dotNetTestRunner.SetupSequence(x => x.GetErrorCode()) .Returns(ErrorCode.FailedTests) .Returns(ErrorCode.Success); - testResultsAnalyzer.SetupSequence(x => x.GetTrxFile(It.IsAny())) - .Returns(firstTrxFile) - .Returns(secondTrxFile) - .Returns(secondTrxFile); - testResultsAnalyzer.Setup(x => x.GetFailedTestsFilter(secondTrxFile)) + testResultsAnalyzer.SetupSequence(x => x.GetTrxFiles(It.IsAny(), It.IsAny())) + .Returns(new []{ firstTrxFile }) + .Returns(new [] { secondTrxFile }); + testResultsAnalyzer.Setup(x => x.GetFailedTestsFilter(It.Is(files => files[0] == firstTrxFile))) .Returns("filterToRerun"); + testResultsAnalyzer.Setup(x => x.GetFailedTestsFilter(It.Is(files => files[0] == secondTrxFile))) + .Returns(string.Empty); testResultsAnalyzer.Setup(x => x.GetReportFiles()) .Returns(new HashSet() {firstTrxFile.FullName, secondTrxFile.FullName}); @@ -241,8 +247,8 @@ public async Task Run_TestsFailOnFirstRun_PassOnSecond_WithDeleteReports() // Assert dotNetTestRunner.Verify(x => x.Test(config, directoryInfo.FullName), Times.Exactly(2)); - testResultsAnalyzer.Verify(x => x.GetTrxFile(It.IsAny()), Times.Exactly(3)); - testResultsAnalyzer.Verify(x => x.GetFailedTestsFilter(secondTrxFile), Times.Exactly(1)); + testResultsAnalyzer.Verify(x => x.GetTrxFiles(It.IsAny(), It.IsAny()), Times.Exactly(2)); + testResultsAnalyzer.Verify(x => x.GetFailedTestsFilter(new[] { secondTrxFile}), Times.Exactly(1)); firstTrxFile.Exists.Should().BeFalse(); secondTrxFile.Exists.Should().BeFalse(); } @@ -272,11 +278,10 @@ public async Task Run_TestsFailOnAllTries_Failure() .Returns(ErrorCode.FailedTests) .Returns(ErrorCode.FailedTests) .Returns(ErrorCode.FailedTests); - testResultsAnalyzer.SetupSequence(x => x.GetTrxFile(It.IsAny())) - .Returns((IFileInfo?)null) - .Returns(firstTrxFile) - .Returns(secondTrxFile); - testResultsAnalyzer.SetupSequence(x => x.GetFailedTestsFilter(It.IsAny())) + testResultsAnalyzer.SetupSequence(x => x.GetTrxFiles(It.IsAny(), It.IsAny())) + .Returns(new [] {firstTrxFile}) + .Returns(new [] {secondTrxFile}); + testResultsAnalyzer.SetupSequence(x => x.GetFailedTestsFilter(It.IsAny())) .Returns("filterToRerun") .Returns("filterToRerun"); @@ -285,8 +290,8 @@ public async Task Run_TestsFailOnAllTries_Failure() // Assert dotNetTestRunner.Verify(x => x.Test(config, directoryInfo.FullName), Times.Exactly(3)); - testResultsAnalyzer.Verify(x => x.GetTrxFile(It.IsAny()), Times.Exactly(3)); - testResultsAnalyzer.Verify(x => x.GetFailedTestsFilter(It.IsAny()), Times.Exactly(2)); + testResultsAnalyzer.Verify(x => x.GetTrxFiles(It.IsAny(), It.IsAny()), Times.Exactly(2)); + testResultsAnalyzer.Verify(x => x.GetFailedTestsFilter(It.IsAny()), Times.Exactly(2)); } private void InitialConfigurationSetup(RerunCommandConfiguration configuration, string extraParams = "")