From d61e015f29642539e4a2a609a00e2656d006171a Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Sat, 16 Nov 2024 12:00:50 -0600 Subject: [PATCH 01/19] Increment versioning for 5.0.* development. --- src/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 5fadd5eb..abbaf02a 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -19,7 +19,7 @@ true true snupkg - 4.1 + 5.0 From 5685304704b2caa277473be00b4614d7bf73bad2 Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Mon, 31 Mar 2025 12:25:33 -0500 Subject: [PATCH 02/19] Convert Solution file format from legacy `*.sln` to modern `*.slnx`. --- .gitattributes | 2 +- src/Fixie.sln | 50 -------------------------------------------------- src/Fixie.slnx | 11 +++++++++++ 3 files changed, 12 insertions(+), 51 deletions(-) delete mode 100644 src/Fixie.sln create mode 100644 src/Fixie.slnx diff --git a/.gitattributes b/.gitattributes index badb96a3..3d0484aa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,7 +5,7 @@ *.ps1 text *.yml text -*.sln text +*.slnx text *.sln.DotSettings text *.props text diff --git a/src/Fixie.sln b/src/Fixie.sln deleted file mode 100644 index d1cddc28..00000000 --- a/src/Fixie.sln +++ /dev/null @@ -1,50 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.8.34309.116 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fixie", "Fixie\Fixie.csproj", "{54B62C1C-3C2A-41A6-9D19-008F814D1791}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fixie.Tests", "Fixie.Tests\Fixie.Tests.csproj", "{79891344-547E-4F8D-959B-07684D09322D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fixie.TestAdapter", "Fixie.TestAdapter\Fixie.TestAdapter.csproj", "{50D2B103-EF46-4438-9720-08C93B6EF714}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fixie.Console", "Fixie.Console\Fixie.Console.csproj", "{04605B99-6D68-4BC5-8239-DC2EF4498B6F}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9F2B08BE-2199-4F4F-95BD-1E7F19CFFF61}" - ProjectSection(SolutionItems) = preProject - Directory.Build.props = Directory.Build.props - Directory.Packages.props = Directory.Packages.props - ..\nuget.config = ..\nuget.config - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {54B62C1C-3C2A-41A6-9D19-008F814D1791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {54B62C1C-3C2A-41A6-9D19-008F814D1791}.Debug|Any CPU.Build.0 = Debug|Any CPU - {54B62C1C-3C2A-41A6-9D19-008F814D1791}.Release|Any CPU.ActiveCfg = Release|Any CPU - {54B62C1C-3C2A-41A6-9D19-008F814D1791}.Release|Any CPU.Build.0 = Release|Any CPU - {79891344-547E-4F8D-959B-07684D09322D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {79891344-547E-4F8D-959B-07684D09322D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {79891344-547E-4F8D-959B-07684D09322D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {79891344-547E-4F8D-959B-07684D09322D}.Release|Any CPU.Build.0 = Release|Any CPU - {50D2B103-EF46-4438-9720-08C93B6EF714}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50D2B103-EF46-4438-9720-08C93B6EF714}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50D2B103-EF46-4438-9720-08C93B6EF714}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50D2B103-EF46-4438-9720-08C93B6EF714}.Release|Any CPU.Build.0 = Release|Any CPU - {04605B99-6D68-4BC5-8239-DC2EF4498B6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {04605B99-6D68-4BC5-8239-DC2EF4498B6F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {04605B99-6D68-4BC5-8239-DC2EF4498B6F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {04605B99-6D68-4BC5-8239-DC2EF4498B6F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {03391B3B-84B6-495B-AE0C-32C802774390} - EndGlobalSection -EndGlobal diff --git a/src/Fixie.slnx b/src/Fixie.slnx new file mode 100644 index 00000000..ffb7ab36 --- /dev/null +++ b/src/Fixie.slnx @@ -0,0 +1,11 @@ + + + + + + + + + + + From c0ce5cb32a6bcd2c625bda1cfc11a8fadd276158 Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Wed, 16 Apr 2025 10:42:52 -0500 Subject: [PATCH 03/19] Move the `Mono.Cecil` dependency from the deprecated `Fixie.TestAdapter` project to the `Fixie` project as a step towards dropping `Fixie.TestAdapter`. --- src/Fixie.TestAdapter/Fixie.TestAdapter.csproj | 1 - src/Fixie/Fixie.csproj | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj b/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj index f1cdc343..747c85f4 100644 --- a/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj +++ b/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj @@ -18,7 +18,6 @@ - diff --git a/src/Fixie/Fixie.csproj b/src/Fixie/Fixie.csproj index 837d7e56..50cd7da2 100644 --- a/src/Fixie/Fixie.csproj +++ b/src/Fixie/Fixie.csproj @@ -15,4 +15,8 @@ + + + + \ No newline at end of file From 780ef7c382111eaa52d40ab7973259c0facd8c46 Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Wed, 16 Apr 2025 11:37:54 -0500 Subject: [PATCH 04/19] Drop `Mono.Cecil` dependency from `Fixie.TestAdapter`'s explicit nuspec, now that the project itself no longer has that dependency. --- src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec b/src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec index 0c19b162..06bf38be 100644 --- a/src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec +++ b/src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec @@ -17,12 +17,10 @@ - - From 1508f2ec91e99f854b4f47ac5461598444bc9f6e Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Wed, 16 Apr 2025 11:48:21 -0500 Subject: [PATCH 05/19] Move SourceLocation and its calculation from the deprecated `Fixie.TestAdapter` project to the `Fixie` project as a step towards dropping `Fixie.TestAdapter`. --- src/Fixie.Tests/TestAdapter/SourceLocationProviderTests.cs | 2 +- src/{Fixie.TestAdapter => Fixie/Internal}/SourceLocation.cs | 2 +- .../Internal}/SourceLocationProvider.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/{Fixie.TestAdapter => Fixie/Internal}/SourceLocation.cs (88%) rename src/{Fixie.TestAdapter => Fixie/Internal}/SourceLocationProvider.cs (99%) diff --git a/src/Fixie.Tests/TestAdapter/SourceLocationProviderTests.cs b/src/Fixie.Tests/TestAdapter/SourceLocationProviderTests.cs index fc4c742c..c2ed30bb 100644 --- a/src/Fixie.Tests/TestAdapter/SourceLocationProviderTests.cs +++ b/src/Fixie.Tests/TestAdapter/SourceLocationProviderTests.cs @@ -1,4 +1,4 @@ -using Fixie.TestAdapter; +using Fixie.Internal; using static Fixie.Tests.Utility; namespace Fixie.Tests.TestAdapter; diff --git a/src/Fixie.TestAdapter/SourceLocation.cs b/src/Fixie/Internal/SourceLocation.cs similarity index 88% rename from src/Fixie.TestAdapter/SourceLocation.cs rename to src/Fixie/Internal/SourceLocation.cs index a508be5a..e43c57c1 100644 --- a/src/Fixie.TestAdapter/SourceLocation.cs +++ b/src/Fixie/Internal/SourceLocation.cs @@ -1,4 +1,4 @@ -namespace Fixie.TestAdapter; +namespace Fixie.Internal; class SourceLocation { diff --git a/src/Fixie.TestAdapter/SourceLocationProvider.cs b/src/Fixie/Internal/SourceLocationProvider.cs similarity index 99% rename from src/Fixie.TestAdapter/SourceLocationProvider.cs rename to src/Fixie/Internal/SourceLocationProvider.cs index 71c9e63b..7553a11e 100644 --- a/src/Fixie.TestAdapter/SourceLocationProvider.cs +++ b/src/Fixie/Internal/SourceLocationProvider.cs @@ -4,7 +4,7 @@ using Mono.Cecil.Cil; using Mono.Cecil.Rocks; -namespace Fixie.TestAdapter; +namespace Fixie.Internal; class SourceLocationProvider { From 3a9554be7d15c8e26bb04bd244d6e455b13afc85 Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Wed, 16 Apr 2025 11:53:37 -0500 Subject: [PATCH 06/19] Move SourceLocation testing files to the `Fixie.Tests.Internal` namespace, to match the new location of the code under test. --- .../{TestAdapter => Internal}/SourceLocationProviderTests.cs | 2 +- .../{TestAdapter => Internal}/SourceLocationSamples.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/Fixie.Tests/{TestAdapter => Internal}/SourceLocationProviderTests.cs (99%) rename src/Fixie.Tests/{TestAdapter => Internal}/SourceLocationSamples.cs (97%) diff --git a/src/Fixie.Tests/TestAdapter/SourceLocationProviderTests.cs b/src/Fixie.Tests/Internal/SourceLocationProviderTests.cs similarity index 99% rename from src/Fixie.Tests/TestAdapter/SourceLocationProviderTests.cs rename to src/Fixie.Tests/Internal/SourceLocationProviderTests.cs index c2ed30bb..cbb43069 100644 --- a/src/Fixie.Tests/TestAdapter/SourceLocationProviderTests.cs +++ b/src/Fixie.Tests/Internal/SourceLocationProviderTests.cs @@ -1,7 +1,7 @@ using Fixie.Internal; using static Fixie.Tests.Utility; -namespace Fixie.Tests.TestAdapter; +namespace Fixie.Tests.Internal; public class SourceLocationProviderTests { diff --git a/src/Fixie.Tests/TestAdapter/SourceLocationSamples.cs b/src/Fixie.Tests/Internal/SourceLocationSamples.cs similarity index 97% rename from src/Fixie.Tests/TestAdapter/SourceLocationSamples.cs rename to src/Fixie.Tests/Internal/SourceLocationSamples.cs index 57b47a60..abae0c28 100644 --- a/src/Fixie.Tests/TestAdapter/SourceLocationSamples.cs +++ b/src/Fixie.Tests/Internal/SourceLocationSamples.cs @@ -1,4 +1,4 @@ -namespace Fixie.Tests.TestAdapter; +namespace Fixie.Tests.Internal; // Avoid changes in this file that would move the well-known line numbers indicated in comments. // Otherwise, these comments and the associated assertions would need to be updated together From 2a47580052779a5cd952a5860513e9c2326fb329 Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Wed, 16 Apr 2025 20:06:31 -0500 Subject: [PATCH 07/19] `TestAdapterReport` in the primary `Fixie` project is responsible for attempting source location lookups, instead of `VsDiscoveryRecorder` in the `Fixie.TestAdapter` project, because the test adapter project is deprecated. --- src/Fixie.TestAdapter/VsDiscoveryRecorder.cs | 30 +-------- src/Fixie.TestAdapter/VsTestDiscoverer.cs | 2 +- .../Internal/PipeMessageSerializationTests.cs | 17 ++++- .../TestAdapter/VsDiscoveryRecorderTests.cs | 66 ++++++++----------- src/Fixie/Internal/EntryPoint.cs | 2 +- src/Fixie/Internal/PipeMessage.cs | 1 + src/Fixie/Internal/SourceLocation.cs | 10 +-- src/Fixie/Internal/SourceLocationProvider.cs | 6 +- src/Fixie/Reports/TestAdapterReport.cs | 24 ++++++- 9 files changed, 76 insertions(+), 82 deletions(-) diff --git a/src/Fixie.TestAdapter/VsDiscoveryRecorder.cs b/src/Fixie.TestAdapter/VsDiscoveryRecorder.cs index 9e97481a..993d7e10 100644 --- a/src/Fixie.TestAdapter/VsDiscoveryRecorder.cs +++ b/src/Fixie.TestAdapter/VsDiscoveryRecorder.cs @@ -1,46 +1,22 @@ using Fixie.Internal; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; namespace Fixie.TestAdapter; -class VsDiscoveryRecorder +class VsDiscoveryRecorder(ITestCaseDiscoverySink discoverySink, string assemblyPath) { - readonly IMessageLogger log; - readonly ITestCaseDiscoverySink discoverySink; - readonly string assemblyPath; - readonly SourceLocationProvider sourceLocationProvider; - - public VsDiscoveryRecorder(IMessageLogger log, ITestCaseDiscoverySink discoverySink, string assemblyPath) - { - this.log = log; - this.discoverySink = discoverySink; - this.assemblyPath = assemblyPath; - - sourceLocationProvider = new SourceLocationProvider(assemblyPath); - } - public void Record(PipeMessage.TestDiscovered testDiscovered) { var test = testDiscovered.Test; - SourceLocation? sourceLocation = null; - - try - { - sourceLocationProvider.TryGetSourceLocation(test, out sourceLocation); - } - catch (Exception exception) - { - log.Error(exception.ToString()); - } - var discoveredTest = new TestCase(test, VsTestExecutor.Uri, assemblyPath) { DisplayName = test }; + var sourceLocation = testDiscovered.SourceLocation; + if (sourceLocation != null) { discoveredTest.CodeFilePath = sourceLocation.CodeFilePath; diff --git a/src/Fixie.TestAdapter/VsTestDiscoverer.cs b/src/Fixie.TestAdapter/VsTestDiscoverer.cs index 20c9d1a3..3a65e9eb 100644 --- a/src/Fixie.TestAdapter/VsTestDiscoverer.cs +++ b/src/Fixie.TestAdapter/VsTestDiscoverer.cs @@ -48,7 +48,7 @@ static void DiscoverTests(IMessageLogger log, ITestCaseDiscoverySink discoverySi pipe.Send(); - var recorder = new VsDiscoveryRecorder(log, discoverySink, assemblyPath); + var recorder = new VsDiscoveryRecorder(discoverySink, assemblyPath); while (true) { diff --git a/src/Fixie.Tests/Internal/PipeMessageSerializationTests.cs b/src/Fixie.Tests/Internal/PipeMessageSerializationTests.cs index 55a6e711..f5af007a 100644 --- a/src/Fixie.Tests/Internal/PipeMessageSerializationTests.cs +++ b/src/Fixie.Tests/Internal/PipeMessageSerializationTests.cs @@ -25,8 +25,21 @@ public void ShouldSerializeExecuteTestsMessage() public void ShouldSerializeTestDiscoveredMessage() { - Expect(new PipeMessage.TestDiscovered { Test = TestClass + ".Pass" }, - "{\"Test\":\"Fixie.Tests.Reports.MessagingTests\\u002BSampleTestClass.Pass\"}"); + Expect(new PipeMessage.TestDiscovered { Test = TestClass + ".Pass", SourceLocation = null }, + """ + {"Test":"Fixie.Tests.Reports.MessagingTests\u002BSampleTestClass.Pass","SourceLocation":null} + """); + + var sourceLocation = new SourceLocation + { + CodeFilePath = "full-path-to-code-file", + LineNumber = 123 + }; + + Expect(new PipeMessage.TestDiscovered { Test = TestClass + ".Pass", SourceLocation = sourceLocation }, + """ + {"Test":"Fixie.Tests.Reports.MessagingTests\u002BSampleTestClass.Pass","SourceLocation":{"CodeFilePath":"full-path-to-code-file","LineNumber":123}} + """); } public void ShouldSerializeTestStartedMessage() diff --git a/src/Fixie.Tests/TestAdapter/VsDiscoveryRecorderTests.cs b/src/Fixie.Tests/TestAdapter/VsDiscoveryRecorderTests.cs index 7fcbb26d..05934aab 100644 --- a/src/Fixie.Tests/TestAdapter/VsDiscoveryRecorderTests.cs +++ b/src/Fixie.Tests/TestAdapter/VsDiscoveryRecorderTests.cs @@ -4,7 +4,6 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Fixie.Tests.Reports; -using static System.IO.Directory; namespace Fixie.Tests.TestAdapter; @@ -17,9 +16,9 @@ public void ShouldMapDiscoveredTestsToVsTestDiscoverySink() var log = new StubMessageLogger(); var discoverySink = new StubTestCaseDiscoverySink(); - var vsDiscoveryRecorder = new VsDiscoveryRecorder(log, discoverySink, assemblyPath); + var vsDiscoveryRecorder = new VsDiscoveryRecorder(discoverySink, assemblyPath); - RecordAnticipatedPipeMessages(vsDiscoveryRecorder); + RecordAnticipatedPipeMessages(assemblyPath, vsDiscoveryRecorder); log.Messages.ShouldMatch([]); @@ -32,62 +31,49 @@ public void ShouldMapDiscoveredTestsToVsTestDiscoverySink() ]); } - public void ShouldDefaultSourceLocationPropertiesWhenSourceInspectionThrows() - { - const string invalidAssemblyPath = "assembly.path.dll"; - - var log = new StubMessageLogger(); - var discoverySink = new StubTestCaseDiscoverySink(); - - var discoveryRecorder = new VsDiscoveryRecorder(log, discoverySink, invalidAssemblyPath); - - RecordAnticipatedPipeMessages(discoveryRecorder); - - var expectedError = - $"Error: {typeof(FileNotFoundException).FullName}: " + - $"Could not find file '{Path.Combine(GetCurrentDirectory(), invalidAssemblyPath)}'."; - log.Messages.ItemsShouldSatisfy([ - x => x.Contains(expectedError).ShouldBe(true), - x => x.Contains(expectedError).ShouldBe(true), - x => x.Contains(expectedError).ShouldBe(true), - x => x.Contains(expectedError).ShouldBe(true), - x => x.Contains(expectedError).ShouldBe(true) - ]); - - discoverySink.TestCases.ItemsShouldSatisfy([ - x => x.ShouldBeDiscoveryTimeTestMissingSourceLocation(TestClass + ".Fail", invalidAssemblyPath), - x => x.ShouldBeDiscoveryTimeTestMissingSourceLocation(TestClass + ".FailByAssertion", invalidAssemblyPath), - x => x.ShouldBeDiscoveryTimeTestMissingSourceLocation(TestClass + ".Pass", invalidAssemblyPath), - x => x.ShouldBeDiscoveryTimeTestMissingSourceLocation(TestClass + ".Skip", invalidAssemblyPath), - x => x.ShouldBeDiscoveryTimeTestMissingSourceLocation(GenericTestClass + ".ShouldBeString", invalidAssemblyPath) - ]); - } - - void RecordAnticipatedPipeMessages(VsDiscoveryRecorder vsDiscoveryRecorder) + void RecordAnticipatedPipeMessages(string assemblyPath, VsDiscoveryRecorder vsDiscoveryRecorder) { + SourceLocationProvider sourceLocationProvider = new(assemblyPath); + + var test = TestClass + ".Fail"; + sourceLocationProvider.TryGetSourceLocation(test, out var sourceLocation).ShouldBe(true); vsDiscoveryRecorder.Record(new PipeMessage.TestDiscovered { - Test = TestClass + ".Fail" + Test = test, + SourceLocation = sourceLocation }); + test = TestClass + ".FailByAssertion"; + sourceLocationProvider.TryGetSourceLocation(test, out sourceLocation).ShouldBe(true); vsDiscoveryRecorder.Record(new PipeMessage.TestDiscovered { - Test = TestClass + ".FailByAssertion" + Test = test, + SourceLocation = sourceLocation }); + test = TestClass + ".Pass"; + sourceLocationProvider.TryGetSourceLocation(test, out sourceLocation).ShouldBe(false); + sourceLocation.ShouldBe(null); vsDiscoveryRecorder.Record(new PipeMessage.TestDiscovered { - Test = TestClass + ".Pass" + Test = test, + SourceLocation = sourceLocation }); + test = TestClass + ".Skip"; + sourceLocationProvider.TryGetSourceLocation(test, out sourceLocation).ShouldBe(true); vsDiscoveryRecorder.Record(new PipeMessage.TestDiscovered { - Test = TestClass + ".Skip" + Test = test, + SourceLocation = sourceLocation }); + test = GenericTestClass + ".ShouldBeString"; + sourceLocationProvider.TryGetSourceLocation(test, out sourceLocation).ShouldBe(true); vsDiscoveryRecorder.Record(new PipeMessage.TestDiscovered { - Test = GenericTestClass + ".ShouldBeString" + Test = test, + SourceLocation = sourceLocation }); } diff --git a/src/Fixie/Internal/EntryPoint.cs b/src/Fixie/Internal/EntryPoint.cs index a687bc4c..1427c68e 100644 --- a/src/Fixie/Internal/EntryPoint.cs +++ b/src/Fixie/Internal/EntryPoint.cs @@ -44,7 +44,7 @@ public static async Task Main(Assembly assembly, string[] customArguments) pipeStream.Connect(); pipeStream.ReadMode = PipeTransmissionMode.Byte; - var testAdapterReport = new TestAdapterReport(pipe); + var testAdapterReport = new TestAdapterReport(environment, pipe); var exitCode = ExitCode.Success; diff --git a/src/Fixie/Internal/PipeMessage.cs b/src/Fixie/Internal/PipeMessage.cs index dea8a91d..ca3ce0d2 100644 --- a/src/Fixie/Internal/PipeMessage.cs +++ b/src/Fixie/Internal/PipeMessage.cs @@ -31,6 +31,7 @@ public class ExecuteTests public class TestDiscovered { public required string Test { get; init; } + public required SourceLocation? SourceLocation { get; init; } } public class TestStarted diff --git a/src/Fixie/Internal/SourceLocation.cs b/src/Fixie/Internal/SourceLocation.cs index e43c57c1..440ec7b0 100644 --- a/src/Fixie/Internal/SourceLocation.cs +++ b/src/Fixie/Internal/SourceLocation.cs @@ -2,12 +2,6 @@ class SourceLocation { - public SourceLocation(string codeFilePath, int lineNumber) - { - CodeFilePath = codeFilePath; - LineNumber = lineNumber; - } - - public string CodeFilePath { get; } - public int LineNumber { get; } + public required string CodeFilePath { get; init; } + public required int LineNumber { get; init; } } \ No newline at end of file diff --git a/src/Fixie/Internal/SourceLocationProvider.cs b/src/Fixie/Internal/SourceLocationProvider.cs index 7553a11e..21106aa2 100644 --- a/src/Fixie/Internal/SourceLocationProvider.cs +++ b/src/Fixie/Internal/SourceLocationProvider.cs @@ -80,7 +80,11 @@ static Dictionary MethodLocations(TypeDefinition type) var sequencePoint = FirstOrDefaultSequencePoint(method); if (sequencePoint != null) - return new SourceLocation(sequencePoint.Document.Url, sequencePoint.StartLine); + return new SourceLocation + { + CodeFilePath = sequencePoint.Document.Url, + LineNumber = sequencePoint.StartLine + }; return null; } diff --git a/src/Fixie/Reports/TestAdapterReport.cs b/src/Fixie/Reports/TestAdapterReport.cs index f626a720..375629fd 100644 --- a/src/Fixie/Reports/TestAdapterReport.cs +++ b/src/Fixie/Reports/TestAdapterReport.cs @@ -2,18 +2,38 @@ namespace Fixie.Reports; -class TestAdapterReport(TestAdapterPipe pipe) : +class TestAdapterReport(TestEnvironment environment, TestAdapterPipe pipe) : IHandler, IHandler, IHandler, IHandler, IHandler { + readonly SourceLocationProvider sourceLocationProvider = new(environment.Assembly.Location); + public Task Handle(TestDiscovered message) { + SourceLocation? sourceLocation = null; + + try + { + sourceLocationProvider.TryGetSourceLocation(message.Test, out sourceLocation); + } + catch (Exception exception) + { + using (Foreground.Yellow) + environment.Console.WriteLine( + $"{GetType().FullName} threw an exception while " + + $"attempting to handle a message of type {typeof(TestDiscovered).FullName}:"); + environment.Console.WriteLine(); + environment.Console.WriteLine(exception.ToString()); + environment.Console.WriteLine(); + } + pipe.Send(new PipeMessage.TestDiscovered { - Test = message.Test + Test = message.Test, + SourceLocation = sourceLocation }); return Task.CompletedTask; From f3c579b625b446598e633603f3d0dc42155d6db1 Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Thu, 17 Apr 2025 12:48:28 -0500 Subject: [PATCH 08/19] Remove deprecated target framework from the `Fixie.Tests` project, phasing out now-unreachable conditional compilation paths. --- .vscode/launch.json | 4 ++-- src/Fixie.Tests/CaseNameTests.cs | 6 +----- src/Fixie.Tests/Console/CommandLineTests.cs | 4 ++-- src/Fixie.Tests/Fixie.Tests.csproj | 2 +- src/Fixie.Tests/Utility.cs | 8 +------- 5 files changed, 7 insertions(+), 17 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index ccf63665..1d129f55 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,9 +10,9 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/src/artifacts/bin/Fixie.Tests/debug_net8.0/Fixie.Tests.dll", + "program": "${workspaceFolder}/src/artifacts/bin/Fixie.Tests/debug/Fixie.Tests.dll", "args": [], - "cwd": "${workspaceFolder}/src/artifacts/bin/Fixie.Tests/debug_net8.0", + "cwd": "${workspaceFolder}/src/artifacts/bin/Fixie.Tests/debug", // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console "console": "internalConsole", "stopAtEntry": false diff --git a/src/Fixie.Tests/CaseNameTests.cs b/src/Fixie.Tests/CaseNameTests.cs index 5e0dfabe..9c064485 100644 --- a/src/Fixie.Tests/CaseNameTests.cs +++ b/src/Fixie.Tests/CaseNameTests.cs @@ -169,11 +169,7 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasSt "StringParametersTestClass.String(\"\\u0006 \\u000E \\u000F \\u0010 \\u0011\")", "StringParametersTestClass.String(\"\\u0012 \\u0013 \\u0014 \\u0015 \\u0016\")", - #if NET8_0 - "StringParametersTestClass.String(\"\\u0017 \\u0018 \\u0019 \\u001A \\u001B\")", - #else - "StringParametersTestClass.String(\"\\u0017 \\u0018 \\u0019 \\u001A\")", - #endif + "StringParametersTestClass.String(\"\\u0017 \\u0018 \\u0019 \\u001A\")", //we omitted the one for 'escape' since it was explicit earlier. "StringParametersTestClass.String(\"\\u001C \\u001D \\u001E \\u001F \\u007F\")", "StringParametersTestClass.String(\"\\u0080 \\u0081 \\u0082 \\u0083 \\u0084\")", diff --git a/src/Fixie.Tests/Console/CommandLineTests.cs b/src/Fixie.Tests/Console/CommandLineTests.cs index 7030350b..ed860cf2 100644 --- a/src/Fixie.Tests/Console/CommandLineTests.cs +++ b/src/Fixie.Tests/Console/CommandLineTests.cs @@ -7,11 +7,11 @@ public class CommandLineTests public void ShouldPartitionRunnerArgumentsFromCustomArguments() { CommandLine.Partition([ - "Example.Tests", "--configuration", "Release", "--framework", "net8.0", + "Example.Tests", "--configuration", "Release", "--framework", "net9.0", "--", "customA", "customB", "customC" ], out var runnerArguments, out var customArguments); - runnerArguments.ShouldMatch(["Example.Tests", "--configuration", "Release", "--framework", "net8.0"]); + runnerArguments.ShouldMatch(["Example.Tests", "--configuration", "Release", "--framework", "net9.0"]); customArguments.ShouldMatch(["customA", "customB", "customC"]); CommandLine.Partition(["Example.Tests", "--", "custom"], out runnerArguments, out customArguments); diff --git a/src/Fixie.Tests/Fixie.Tests.csproj b/src/Fixie.Tests/Fixie.Tests.csproj index 3cf5bdaf..83960efb 100644 --- a/src/Fixie.Tests/Fixie.Tests.csproj +++ b/src/Fixie.Tests/Fixie.Tests.csproj @@ -3,7 +3,7 @@ - net8.0;net9.0 + net9.0 diff --git a/src/Fixie.Tests/Utility.cs b/src/Fixie.Tests/Utility.cs index de1af9c1..7c63a49e 100644 --- a/src/Fixie.Tests/Utility.cs +++ b/src/Fixie.Tests/Utility.cs @@ -9,13 +9,7 @@ public static class Utility public static TestEnvironment GetTestEnvironment(TextWriter console) => new(typeof(TestProject).Assembly, null, console, customArguments: []); - public const string TargetFrameworkVersion = - #if NET8_0 - "8.0" - #elif NET9_0 - "9.0" - #endif - ; + public const string TargetFrameworkVersion = "9.0"; public static string FullName() { From 554c605a2165d530ef731a9f47af49f214bb6158 Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Thu, 17 Apr 2025 13:17:04 -0500 Subject: [PATCH 09/19] Remove deprecated target framework from the `Fixie.TestAdapter` project. --- src/Fixie.TestAdapter/Fixie.TestAdapter.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj b/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj index 747c85f4..3d9f49df 100644 --- a/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj +++ b/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj @@ -11,7 +11,7 @@ - net8.0;net9.0 + net9.0 Visual Studio integration for the Fixie test framework. Fixie.TestAdapter.nuspec true From 70ffa4f5823c962b598d95d3eb46fb6e878b77fd Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Thu, 17 Apr 2025 13:27:23 -0500 Subject: [PATCH 10/19] Remove deprecated target framework from the `Fixie.TestAdapter` nuspec, so that it aligns with the corresponding csproj. --- src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec b/src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec index 06bf38be..90465dc8 100644 --- a/src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec +++ b/src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec @@ -15,10 +15,6 @@ $copyright$ - - - - @@ -31,9 +27,7 @@ - - - - + + \ No newline at end of file From be187a4b09523bf689d617072f89ba95573a2769 Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Thu, 17 Apr 2025 13:31:22 -0500 Subject: [PATCH 11/19] Remove deprecated target framework from the `Fixie` project. --- src/Fixie/Fixie.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Fixie/Fixie.csproj b/src/Fixie/Fixie.csproj index 50cd7da2..ad2c0692 100644 --- a/src/Fixie/Fixie.csproj +++ b/src/Fixie/Fixie.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0 + net9.0 Ergonomic Testing for .NET true README.md From bdde7ff5eadca3e40f4da88b934bbba9499ef0e1 Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Thu, 17 Apr 2025 13:37:23 -0500 Subject: [PATCH 12/19] Raise deprecated target framework in the `Fixie.Console` project to the lowest target framework found across all projects, allowing `RollForward` to provide cover for the supported framework range. --- src/Fixie.Console/Fixie.Console.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Fixie.Console/Fixie.Console.csproj b/src/Fixie.Console/Fixie.Console.csproj index e78f4898..e472485b 100644 --- a/src/Fixie.Console/Fixie.Console.csproj +++ b/src/Fixie.Console/Fixie.Console.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 true fixie `dotnet fixie` console test runner for the Fixie test framework. From 3db26f7fce81e7c9dad9d0b4e3db7ad84929351a Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Thu, 17 Apr 2025 14:12:53 -0500 Subject: [PATCH 13/19] Phase out `NET9_0_OR_GREATER` conditional compilation now that all supported target frameworks meet that condition. --- src/Fixie.Tests/CaseNameTests.cs | 17 ----------------- src/Fixie/Internal/CaseNameBuilder.cs | 4 ---- 2 files changed, 21 deletions(-) diff --git a/src/Fixie.Tests/CaseNameTests.cs b/src/Fixie.Tests/CaseNameTests.cs index 9c064485..9967d6f7 100644 --- a/src/Fixie.Tests/CaseNameTests.cs +++ b/src/Fixie.Tests/CaseNameTests.cs @@ -39,9 +39,7 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasCh foreach (var c in new[] {'\\', '\0', '\a', '\b', '\f', '\n', '\r', '\t', '\v'}) await Run(test, c); - #if NET9_0_OR_GREATER await Run(test, '\e'); - #endif foreach (var c in new[] {'\u0000', '\u0085', '\u2028', '\u2029', '\u263A'}) await Run(test, c); @@ -56,18 +54,14 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasCh await Run(test, c); foreach (var c in UnicodeEscapedCharacters() - #if NET9_0_OR_GREATER .Where(c => c != '\e') - #endif ) await Run(test, c); }); var unicodeEscapeExpectations = UnicodeEscapedCharacters() - #if NET9_0_OR_GREATER .Where(c => c != '\e') - #endif .Select(c => $""" CharParametersTestClass.Char('\u{(int)c:X4}') @@ -87,10 +81,7 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasCh "CharParametersTestClass.Char('\\r')", "CharParametersTestClass.Char('\\t')", "CharParametersTestClass.Char('\\v')", - - #if NET9_0_OR_GREATER "CharParametersTestClass.Char('\\e')", - #endif "CharParametersTestClass.Char('\\0')", "CharParametersTestClass.Char('\\u0085')", @@ -132,10 +123,7 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasSt await Run(test, " \' ' \" "); await Run(test, " \\ \0 \a \b "); await Run(test, " \f \n \r \t \v "); - - #if NET9_0_OR_GREATER await Run(test, " \e "); - #endif await Run(test, " \u0000 \u0085 \u2028 \u2029 \u263A "); await Run(test, " \x0000 \x000 \x00 \x0 "); @@ -144,9 +132,7 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasSt foreach (var c in UnicodeEscapedCharacters().Chunk(5) - #if NET9_0_OR_GREATER .Select(chunk => chunk.Where(c => c != '\e')) - #endif .Select(chunk => string.Join(' ', chunk))) await Run(test, c); @@ -156,10 +142,7 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasSt "StringParametersTestClass.String(\" ' ' \\\" \")", "StringParametersTestClass.String(\" \\\\ \\0 \\a \\b \")", "StringParametersTestClass.String(\" \\f \\n \\r \\t \\v \")", - - #if NET9_0_OR_GREATER "StringParametersTestClass.String(\" \\e \")", - #endif "StringParametersTestClass.String(\" \\0 \\u0085 \\u2028 \\u2029 ☺ \")", "StringParametersTestClass.String(\" \\0 \\0 \\0 \\0 \")", diff --git a/src/Fixie/Internal/CaseNameBuilder.cs b/src/Fixie/Internal/CaseNameBuilder.cs index 1950d1a2..cfe94497 100644 --- a/src/Fixie/Internal/CaseNameBuilder.cs +++ b/src/Fixie/Internal/CaseNameBuilder.cs @@ -76,11 +76,7 @@ static string Escape(char ch, Literal literal) => '\v' => @"\v", '\f' => @"\f", '\r' => @"\r", - - #if NET9_0_OR_GREATER '\e' => @"\e", - #endif - ' ' => " ", '\"' => literal == Literal.String ? @"\""" : char.ToString(ch), '\'' => literal == Literal.Character ? @"\'" : char.ToString(ch), From 32377e377f5714aa3327414692b0ab2b5d963a32 Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Thu, 17 Apr 2025 15:05:03 -0500 Subject: [PATCH 14/19] Simplify case name test coverage of character serialization, now that all supported target frameworks recognize the escape sequence `\e` representing the escape character otherwise known as `\001B`. --- src/Fixie.Tests/CaseNameTests.cs | 50 ++++++++++++-------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/src/Fixie.Tests/CaseNameTests.cs b/src/Fixie.Tests/CaseNameTests.cs index 9967d6f7..5bb965c8 100644 --- a/src/Fixie.Tests/CaseNameTests.cs +++ b/src/Fixie.Tests/CaseNameTests.cs @@ -36,11 +36,9 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasCh foreach (var c in new[] {'\"', '"', '\''}) await Run(test, c); - foreach (var c in new[] {'\\', '\0', '\a', '\b', '\f', '\n', '\r', '\t', '\v'}) + foreach (var c in new[] {'\\', '\0', '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\e'}) await Run(test, c); - await Run(test, '\e'); - foreach (var c in new[] {'\u0000', '\u0085', '\u2028', '\u2029', '\u263A'}) await Run(test, c); @@ -53,16 +51,11 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasCh foreach (var c in new[] {'\U00000000', '\U00000085', '\U00002028', '\U00002029', '\U0000263A'}) await Run(test, c); - foreach (var c in UnicodeEscapedCharacters() - .Where(c => c != '\e') - ) + foreach (var c in UnicodeEscapedCharacters()) await Run(test, c); }); var unicodeEscapeExpectations = UnicodeEscapedCharacters() - - .Where(c => c != '\e') - .Select(c => $""" CharParametersTestClass.Char('\u{(int)c:X4}') """); @@ -122,8 +115,7 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasSt { await Run(test, " \' ' \" "); await Run(test, " \\ \0 \a \b "); - await Run(test, " \f \n \r \t \v "); - await Run(test, " \e "); + await Run(test, " \f \n \r \t \v \e "); await Run(test, " \u0000 \u0085 \u2028 \u2029 \u263A "); await Run(test, " \x0000 \x000 \x00 \x0 "); @@ -131,9 +123,6 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasSt await Run(test, " \U00000000 \U00000085 \U00002028 \U00002029 \U0000263A "); foreach (var c in UnicodeEscapedCharacters().Chunk(5) - - .Select(chunk => chunk.Where(c => c != '\e')) - .Select(chunk => string.Join(' ', chunk))) await Run(test, c); }); @@ -141,8 +130,7 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasSt ShouldHaveNames(output, "StringParametersTestClass.String(\" ' ' \\\" \")", "StringParametersTestClass.String(\" \\\\ \\0 \\a \\b \")", - "StringParametersTestClass.String(\" \\f \\n \\r \\t \\v \")", - "StringParametersTestClass.String(\" \\e \")", + "StringParametersTestClass.String(\" \\f \\n \\r \\t \\v \\e \")", "StringParametersTestClass.String(\" \\0 \\u0085 \\u2028 \\u2029 ☺ \")", "StringParametersTestClass.String(\" \\0 \\0 \\0 \\0 \")", @@ -152,20 +140,19 @@ public async Task ShouldIncludeEscapeSequencesInNameWhenTheUnderlyingMethodHasSt "StringParametersTestClass.String(\"\\u0006 \\u000E \\u000F \\u0010 \\u0011\")", "StringParametersTestClass.String(\"\\u0012 \\u0013 \\u0014 \\u0015 \\u0016\")", - "StringParametersTestClass.String(\"\\u0017 \\u0018 \\u0019 \\u001A\")", //we omitted the one for 'escape' since it was explicit earlier. - - "StringParametersTestClass.String(\"\\u001C \\u001D \\u001E \\u001F \\u007F\")", - "StringParametersTestClass.String(\"\\u0080 \\u0081 \\u0082 \\u0083 \\u0084\")", - "StringParametersTestClass.String(\"\\u0085 \\u0086 \\u0087 \\u0088 \\u0089\")", - "StringParametersTestClass.String(\"\\u008A \\u008B \\u008C \\u008D \\u008E\")", - "StringParametersTestClass.String(\"\\u008F \\u0090 \\u0091 \\u0092 \\u0093\")", - "StringParametersTestClass.String(\"\\u0094 \\u0095 \\u0096 \\u0097 \\u0098\")", - "StringParametersTestClass.String(\"\\u0099 \\u009A \\u009B \\u009C \\u009D\")", - "StringParametersTestClass.String(\"\\u009E \\u009F \\u0085 \\u00A0 \\u1680\")", - "StringParametersTestClass.String(\"\\u2000 \\u2001 \\u2002 \\u2003 \\u2004\")", - "StringParametersTestClass.String(\"\\u2005 \\u2006 \\u2007 \\u2008 \\u2009\")", - "StringParametersTestClass.String(\"\\u200A \\u2028 \\u2029 \\u202F \\u205F\")", - "StringParametersTestClass.String(\"\\u3000\")" + "StringParametersTestClass.String(\"\\u0017 \\u0018 \\u0019 \\u001A \\u001C\")", + + "StringParametersTestClass.String(\"\\u001D \\u001E \\u001F \\u007F \\u0080\")", + "StringParametersTestClass.String(\"\\u0081 \\u0082 \\u0083 \\u0084 \\u0085\")", + "StringParametersTestClass.String(\"\\u0086 \\u0087 \\u0088 \\u0089 \\u008A\")", + "StringParametersTestClass.String(\"\\u008B \\u008C \\u008D \\u008E \\u008F\")", + "StringParametersTestClass.String(\"\\u0090 \\u0091 \\u0092 \\u0093 \\u0094\")", + "StringParametersTestClass.String(\"\\u0095 \\u0096 \\u0097 \\u0098 \\u0099\")", + "StringParametersTestClass.String(\"\\u009A \\u009B \\u009C \\u009D \\u009E\")", + "StringParametersTestClass.String(\"\\u009F \\u0085 \\u00A0 \\u1680 \\u2000\")", + "StringParametersTestClass.String(\"\\u2001 \\u2002 \\u2003 \\u2004 \\u2005\")", + "StringParametersTestClass.String(\"\\u2006 \\u2007 \\u2008 \\u2009 \\u200A\")", + "StringParametersTestClass.String(\"\\u2028 \\u2029 \\u202F \\u205F \\u3000\")" ); } @@ -277,7 +264,8 @@ static IEnumerable UnicodeEscapedCharacters() // '\uHHHH' hex escape sequences. for (char c = '\u0001'; c <= '\u0006'; c++) yield return c; - for (char c = '\u000E'; c <= '\u001F'; c++) yield return c; + for (char c = '\u000E'; c <= '\u001A'; c++) yield return c; + for (char c = '\u001C'; c <= '\u001F'; c++) yield return c; yield return '\u007F'; for (char c = '\u0080'; c <= '\u009F'; c++) yield return c; From 56a0c5000a535dca028026ac9dabb20858b76882 Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Thu, 17 Apr 2025 19:34:42 -0500 Subject: [PATCH 15/19] Stop packing the deprecated VSTest adapter. --- .gitattributes | 1 - build.ps1 | 1 - .../Fixie.TestAdapter.csproj | 12 ------- .../Fixie.TestAdapter.nuspec | 33 ------------------- 4 files changed, 47 deletions(-) delete mode 100644 src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec diff --git a/.gitattributes b/.gitattributes index 3d0484aa..52d4937c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14,4 +14,3 @@ *.csproj text *.cs text diff=csharp *.fs text -*.nuspec text diff --git a/build.ps1 b/build.ps1 index 66442e9a..be9d4f43 100644 --- a/build.ps1 +++ b/build.ps1 @@ -19,5 +19,4 @@ step { dotnet $fixie *.Tests -c Release --no-build } if ($pack) { step { dotnet pack src/Fixie -o packages -c Release --no-build --nologo } step { dotnet pack src/Fixie.Console -o packages -c Release --no-build --nologo } - step { dotnet pack src/Fixie.TestAdapter -o packages -c Release --no-build --nologo } } \ No newline at end of file diff --git a/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj b/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj index 3d9f49df..2e5b4934 100644 --- a/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj +++ b/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj @@ -1,20 +1,8 @@  - - - $(NuspecProperties);id=$(PackageId) - $(NuspecProperties);version=$(PackageVersion) - $(NuspecProperties);authors=$(Authors) - $(NuspecProperties);description=$(Description) - $(NuspecProperties);copyright=$(Copyright) - - - net9.0 Visual Studio integration for the Fixie test framework. - Fixie.TestAdapter.nuspec - true diff --git a/src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec b/src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec deleted file mode 100644 index 90465dc8..00000000 --- a/src/Fixie.TestAdapter/Fixie.TestAdapter.nuspec +++ /dev/null @@ -1,33 +0,0 @@ - - - - - $id$ - $version$ - $authors$ - $authors$ - false - README.md - MIT - https://fixie.github.io - icon.png - $description$ - $copyright$ - - - - - - - - - - - - - - - - - - \ No newline at end of file From e1637e4075b10d1f7aa3bf6e27f54083989b459a Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Thu, 17 Apr 2025 20:18:21 -0500 Subject: [PATCH 16/19] Drop deprecated interprocess communication between the test assembly process and the VSTest adapter process. To limit the scope of this change to the interprocess communication mechanism and its immediate "footprint", the scope was to begin by deleting `PipeMessage.cs` and then remove anything that became an immediate compiler error or test failure until we could get back to a valid build with all passing tests. As of this commit, there are remaining sections of dead code to be removed in subsequent commits, allowing this commit to represent the interprocess communciation mechanism itself should it or something like it need to be restored later. --- src/Fixie.TestAdapter/RunnerException.cs | 17 +- src/Fixie.TestAdapter/VsDiscoveryRecorder.cs | 28 -- src/Fixie.TestAdapter/VsExecutionRecorder.cs | 77 ----- src/Fixie.TestAdapter/VsTestDiscoverer.cs | 48 +--- src/Fixie.TestAdapter/VsTestExecutor.cs | 118 +------- .../Internal/PipeMessageSerializationTests.cs | 122 -------- .../TestAdapter/VsDiscoveryRecorderTests.cs | 95 ------ .../TestAdapter/VsExecutionRecorderTests.cs | 270 ------------------ src/Fixie/Internal/EntryPoint.cs | 70 +---- src/Fixie/Internal/PipeMessage.cs | 83 ------ src/Fixie/Internal/TestAdapterPipe.cs | 80 ------ src/Fixie/Reports/TestAdapterReport.cs | 89 ------ 12 files changed, 12 insertions(+), 1085 deletions(-) delete mode 100644 src/Fixie.TestAdapter/VsDiscoveryRecorder.cs delete mode 100644 src/Fixie.TestAdapter/VsExecutionRecorder.cs delete mode 100644 src/Fixie.Tests/Internal/PipeMessageSerializationTests.cs delete mode 100644 src/Fixie.Tests/TestAdapter/VsDiscoveryRecorderTests.cs delete mode 100644 src/Fixie.Tests/TestAdapter/VsExecutionRecorderTests.cs delete mode 100644 src/Fixie/Internal/PipeMessage.cs delete mode 100644 src/Fixie/Internal/TestAdapterPipe.cs delete mode 100644 src/Fixie/Reports/TestAdapterReport.cs diff --git a/src/Fixie.TestAdapter/RunnerException.cs b/src/Fixie.TestAdapter/RunnerException.cs index df0b79f4..d409d1f9 100644 --- a/src/Fixie.TestAdapter/RunnerException.cs +++ b/src/Fixie.TestAdapter/RunnerException.cs @@ -1,7 +1,4 @@ -using System.Text; -using Fixie.Internal; - -namespace Fixie.TestAdapter; +namespace Fixie.TestAdapter; class RunnerException : Exception { @@ -9,16 +6,4 @@ public RunnerException(Exception exception) : base(exception.ToString()) { } - - public RunnerException(PipeMessage.Exception exception) - : base(new StringBuilder() - .AppendLine() - .AppendLine() - .AppendLine(exception.Message) - .AppendLine() - .AppendLine(exception.Type) - .AppendLine(exception.StackTrace) - .ToString()) - { - } } \ No newline at end of file diff --git a/src/Fixie.TestAdapter/VsDiscoveryRecorder.cs b/src/Fixie.TestAdapter/VsDiscoveryRecorder.cs deleted file mode 100644 index 993d7e10..00000000 --- a/src/Fixie.TestAdapter/VsDiscoveryRecorder.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Fixie.Internal; -using Microsoft.VisualStudio.TestPlatform.ObjectModel; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; - -namespace Fixie.TestAdapter; - -class VsDiscoveryRecorder(ITestCaseDiscoverySink discoverySink, string assemblyPath) -{ - public void Record(PipeMessage.TestDiscovered testDiscovered) - { - var test = testDiscovered.Test; - - var discoveredTest = new TestCase(test, VsTestExecutor.Uri, assemblyPath) - { - DisplayName = test - }; - - var sourceLocation = testDiscovered.SourceLocation; - - if (sourceLocation != null) - { - discoveredTest.CodeFilePath = sourceLocation.CodeFilePath; - discoveredTest.LineNumber = sourceLocation.LineNumber; - } - - discoverySink.SendTestCase(discoveredTest); - } -} \ No newline at end of file diff --git a/src/Fixie.TestAdapter/VsExecutionRecorder.cs b/src/Fixie.TestAdapter/VsExecutionRecorder.cs deleted file mode 100644 index c279ed7a..00000000 --- a/src/Fixie.TestAdapter/VsExecutionRecorder.cs +++ /dev/null @@ -1,77 +0,0 @@ -using Fixie.Internal; -using Microsoft.VisualStudio.TestPlatform.ObjectModel; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; -using static System.Environment; - -namespace Fixie.TestAdapter; - -using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult; - -class VsExecutionRecorder -{ - readonly ITestExecutionRecorder log; - readonly string assemblyPath; - - public VsExecutionRecorder(ITestExecutionRecorder log, string assemblyPath) - { - this.log = log; - this.assemblyPath = assemblyPath; - } - - public void Record(PipeMessage.TestStarted message) - { - var testCase = ToVsTestCase(message.Test); - - log.RecordStart(testCase); - } - - public void Record(PipeMessage.TestSkipped result) - { - Record(result, x => - { - x.Outcome = TestOutcome.Skipped; - x.ErrorMessage = result.Reason; - }); - } - - public void Record(PipeMessage.TestPassed result) - { - Record(result, x => - { - x.Outcome = TestOutcome.Passed; - }); - } - - public void Record(PipeMessage.TestFailed result) - { - Record(result, x => - { - x.Outcome = TestOutcome.Failed; - x.ErrorMessage = result.Reason.Message; - x.ErrorStackTrace = result.Reason.Type + - NewLine + - result.Reason.StackTrace; - }); - } - - void Record(PipeMessage.TestCompleted result, Action customize) - { - var testCase = ToVsTestCase(result.Test); - - var testResult = new TestResult(testCase) - { - DisplayName = result.TestCase, - Duration = TimeSpan.FromMilliseconds(result.DurationInMilliseconds), - ComputerName = MachineName - }; - - customize(testResult); - - log.RecordResult(testResult); - } - - TestCase ToVsTestCase(string test) - { - return new TestCase(test, VsTestExecutor.Uri, assemblyPath); - } -} \ No newline at end of file diff --git a/src/Fixie.TestAdapter/VsTestDiscoverer.cs b/src/Fixie.TestAdapter/VsTestDiscoverer.cs index 3a65e9eb..417ee133 100644 --- a/src/Fixie.TestAdapter/VsTestDiscoverer.cs +++ b/src/Fixie.TestAdapter/VsTestDiscoverer.cs @@ -1,6 +1,4 @@ -using System.IO.Pipes; -using Fixie.Internal; -using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using static Fixie.TestAdapter.TestAssembly; @@ -36,49 +34,5 @@ static void DiscoverTests(IMessageLogger log, ITestCaseDiscoverySink discoverySi } log.Info("Processing " + assemblyPath); - - var pipeName = Guid.NewGuid().ToString(); - Environment.SetEnvironmentVariable("FIXIE_NAMED_PIPE", pipeName); - - using (var pipeStream = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte)) - using (var pipe = new TestAdapterPipe(pipeStream)) - using (var process = StartDiscovery(assemblyPath)) - { - pipeStream.WaitForConnection(); - - pipe.Send(); - - var recorder = new VsDiscoveryRecorder(discoverySink, assemblyPath); - - while (true) - { - var messageType = pipe.ReceiveMessageType(); - - if (messageType == typeof(PipeMessage.TestDiscovered).FullName) - { - var testDiscovered = pipe.Receive(); - recorder.Record(testDiscovered); - } - else if (messageType == typeof(PipeMessage.Exception).FullName) - { - var exception = pipe.Receive(); - throw new RunnerException(exception); - } - else if (messageType == typeof(PipeMessage.EndOfPipe).FullName) - { - var endOfPipe = pipe.Receive(); - break; - } - else if (!string.IsNullOrEmpty(messageType)) - { - var body = pipe.ReceiveMessageBody(); - log.Error($"The test runner received an unexpected message of type {messageType}: {body}"); - } - else - { - throw new TestProcessExitException(process.TryGetExitCode()); - } - } - } } } \ No newline at end of file diff --git a/src/Fixie.TestAdapter/VsTestExecutor.cs b/src/Fixie.TestAdapter/VsTestExecutor.cs index 6fd67ba7..6fd113cf 100644 --- a/src/Fixie.TestAdapter/VsTestExecutor.cs +++ b/src/Fixie.TestAdapter/VsTestExecutor.cs @@ -1,6 +1,4 @@ -using System.IO.Pipes; -using Fixie.Internal; -using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using static Fixie.TestAdapter.TestAssembly; @@ -37,13 +35,8 @@ public void RunTests(IEnumerable? sources, IRunContext? runContext, IFra HandlePoorVsTestImplementationDetails(runContext, frameworkHandle); - var executeTests = new PipeMessage.ExecuteTests - { - Filter = [] - }; - foreach (var assemblyPath in sources) - RunTests(log, frameworkHandle, assemblyPath, executeTests); + RunTests(log, frameworkHandle, assemblyPath); } catch (Exception exception) { @@ -80,12 +73,7 @@ public void RunTests(IEnumerable? tests, IRunContext? runContext, IFra { var assemblyPath = assemblyGroup.Key; - var executeTests = new PipeMessage.ExecuteTests - { - Filter = assemblyGroup.Select(x => x.FullyQualifiedName).ToArray() - }; - - RunTests(log, frameworkHandle, assemblyPath, executeTests); + RunTests(log, frameworkHandle, assemblyPath); } } catch (Exception exception) @@ -96,7 +84,7 @@ public void RunTests(IEnumerable? tests, IRunContext? runContext, IFra public void Cancel() { } - static void RunTests(IMessageLogger log, IFrameworkHandle frameworkHandle, string assemblyPath, PipeMessage.ExecuteTests executeTests) + static void RunTests(IMessageLogger log, IFrameworkHandle frameworkHandle, string assemblyPath) { if (!IsTestAssembly(assemblyPath)) { @@ -105,104 +93,6 @@ static void RunTests(IMessageLogger log, IFrameworkHandle frameworkHandle, strin } log.Info("Processing " + assemblyPath); - - var pipeName = Guid.NewGuid().ToString(); - Environment.SetEnvironmentVariable("FIXIE_NAMED_PIPE", pipeName); - - using (var pipeStream = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte)) - using (var pipe = new TestAdapterPipe(pipeStream)) - using (var process = StartExecution(assemblyPath, frameworkHandle, out var attachmentFailure)) - { - pipeStream.WaitForConnection(); - - pipe.Send(executeTests); - - var recorder = new VsExecutionRecorder(frameworkHandle, assemblyPath); - - PipeMessage.TestStarted? lastTestStarted = null; - - while (true) - { - var messageType = pipe.ReceiveMessageType(); - - if (messageType == typeof(PipeMessage.TestStarted).FullName) - { - var message = pipe.Receive(); - lastTestStarted = message; - recorder.Record(message); - } - else if (messageType == typeof(PipeMessage.TestSkipped).FullName) - { - var testResult = pipe.Receive(); - recorder.Record(testResult); - } - else if (messageType == typeof(PipeMessage.TestPassed).FullName) - { - var testResult = pipe.Receive(); - recorder.Record(testResult); - } - else if (messageType == typeof(PipeMessage.TestFailed).FullName) - { - var testResult = pipe.Receive(); - recorder.Record(testResult); - } - else if (messageType == typeof(PipeMessage.Exception).FullName) - { - var exception = pipe.Receive(); - throw new RunnerException(exception); - } - else if (messageType == typeof(PipeMessage.EndOfPipe).FullName) - { - var endOfPipe = pipe.Receive(); - break; - } - else if (!string.IsNullOrEmpty(messageType)) - { - var body = pipe.ReceiveMessageBody(); - log.Error($"The test runner received an unexpected message of type {messageType}: {body}"); - } - else - { - var exception = new TestProcessExitException(process.TryGetExitCode()); - - if (lastTestStarted != null) - { - recorder.Record(new PipeMessage.TestFailed - { - Test = lastTestStarted.Test, - TestCase = lastTestStarted.Test, - Reason = new PipeMessage.Exception(exception), - DurationInMilliseconds = 0 - }); - } - - throw exception; - } - } - - if (attachmentFailure != null) - { - var exception = attachmentFailure.ThirdPartyTestHostException; - - var reason = new PipeMessage.Exception - { - Type = exception.GetType().FullName!, - Message = attachmentFailure.Message, - StackTrace = exception.StackTrace! - }; - - foreach (var selectedTest in executeTests.Filter) - { - recorder.Record(new PipeMessage.TestFailed - { - Test = selectedTest, - TestCase = selectedTest, - Reason = reason, - DurationInMilliseconds = 0 - }); - } - } - } } static void HandlePoorVsTestImplementationDetails(IRunContext? runContext, IFrameworkHandle frameworkHandle) diff --git a/src/Fixie.Tests/Internal/PipeMessageSerializationTests.cs b/src/Fixie.Tests/Internal/PipeMessageSerializationTests.cs deleted file mode 100644 index f5af007a..00000000 --- a/src/Fixie.Tests/Internal/PipeMessageSerializationTests.cs +++ /dev/null @@ -1,122 +0,0 @@ -using Fixie.Internal; -using Fixie.Tests.Reports; - -namespace Fixie.Tests.Internal; - -public class PipeMessageSerializationTests : MessagingTests -{ - public void ShouldSerializeDiscoverTestsMessage() - { - Expect(new PipeMessage.DiscoverTests(), "{}"); - } - - public void ShouldSerializeExecuteTestsMessage() - { - Expect(new PipeMessage.ExecuteTests { Filter = [] }, - "{\"Filter\":[]}"); - - Expect(new PipeMessage.ExecuteTests { Filter = - [ - TestClass + ".Pass", - GenericTestClass + ".ShouldBeString" - ] }, - "{\"Filter\":[\"Fixie.Tests.Reports.MessagingTests\\u002BSampleTestClass.Pass\",\"Fixie.Tests.Reports.MessagingTests\\u002BSampleGenericTestClass.ShouldBeString\"]}"); - } - - public void ShouldSerializeTestDiscoveredMessage() - { - Expect(new PipeMessage.TestDiscovered { Test = TestClass + ".Pass", SourceLocation = null }, - """ - {"Test":"Fixie.Tests.Reports.MessagingTests\u002BSampleTestClass.Pass","SourceLocation":null} - """); - - var sourceLocation = new SourceLocation - { - CodeFilePath = "full-path-to-code-file", - LineNumber = 123 - }; - - Expect(new PipeMessage.TestDiscovered { Test = TestClass + ".Pass", SourceLocation = sourceLocation }, - """ - {"Test":"Fixie.Tests.Reports.MessagingTests\u002BSampleTestClass.Pass","SourceLocation":{"CodeFilePath":"full-path-to-code-file","LineNumber":123}} - """); - } - - public void ShouldSerializeTestStartedMessage() - { - Expect(new PipeMessage.TestStarted { Test = TestClass + ".Pass" }, - "{\"Test\":\"Fixie.Tests.Reports.MessagingTests\\u002BSampleTestClass.Pass\"}"); - } - - public void ShouldSerializeTestSkippedMessage() - { - Expect(new PipeMessage.TestSkipped - { - Reason = "⚠ Skipped!", - Test = GenericTestClass + ".ShouldBeString", - TestCase = GenericTestClass + ".ShouldBeString(123)", - DurationInMilliseconds = 123.456d - }, - "{\"Reason\":\"\\u26A0 Skipped!\",\"Test\":\"Fixie.Tests.Reports.MessagingTests\\u002BSampleGenericTestClass.ShouldBeString\",\"TestCase\":\"Fixie.Tests.Reports.MessagingTests\\u002BSampleGenericTestClass.ShouldBeString\\u003CSystem.Int32\\u003E(123)\",\"DurationInMilliseconds\":123.456}"); - } - - public void ShouldSerializeTestPassedMessage() - { - Expect(new PipeMessage.TestPassed - { - Test = GenericTestClass + ".ShouldBeString", - TestCase = GenericTestClass + ".ShouldBeString(123)", - DurationInMilliseconds = 123.456d - }, - "{\"Test\":\"Fixie.Tests.Reports.MessagingTests\\u002BSampleGenericTestClass.ShouldBeString\",\"TestCase\":\"Fixie.Tests.Reports.MessagingTests\\u002BSampleGenericTestClass.ShouldBeString\\u003CSystem.Int32\\u003E(123)\",\"DurationInMilliseconds\":123.456}"); - } - - public void ShouldSerializeTestFailedMessage() - { - var at = At("ShouldBeString[T](T genericArgument)").Replace("\\", "\\\\"); - - Expect(new PipeMessage.TestFailed - { - Reason = new PipeMessage.Exception - { - Type = "Fixie.Assertions.AssertException", - Message = "Expected: System.String\nActual: System.Int32", - StackTrace = At("ShouldBeString[T](T genericArgument)") - }, - Test = GenericTestClass + ".ShouldBeString", - TestCase = GenericTestClass + ".ShouldBeString(123)", - DurationInMilliseconds = 123.456d - }, - "{\"Reason\":{\"Type\":\"Fixie.Assertions.AssertException\",\"Message\":\"Expected: System.String\\nActual: System.Int32\",\"StackTrace\":\"" + at + "\"},\"Test\":\"Fixie.Tests.Reports.MessagingTests\\u002BSampleGenericTestClass.ShouldBeString\",\"TestCase\":\"Fixie.Tests.Reports.MessagingTests\\u002BSampleGenericTestClass.ShouldBeString\\u003CSystem.Int32\\u003E(123)\",\"DurationInMilliseconds\":123.456}"); - } - - public void ShouldSerializeExceptionMessage() - { - var at = At("ShouldBeString[T](T genericArgument)").Replace("\\", "\\\\"); - - Expect(new PipeMessage.Exception - { - Type = "Fixie.Assertions.AssertException", - Message = "Expected: System.String\nActual: System.Int32", - StackTrace = At("ShouldBeString[T](T genericArgument)") - }, - "{\"Type\":\"Fixie.Assertions.AssertException\",\"Message\":\"Expected: System.String\\nActual: System.Int32\",\"StackTrace\":\"" + at + "\"}"); - } - - public void ShouldSerializeEndOfPipeMessage() - { - Expect(new PipeMessage.EndOfPipe(), "{}"); - } - - public void ShouldThrowForNullDeserialization() - { - var nullMessage = () => PipeMessage.Deserialize("null"); - nullMessage.ShouldThrow("Message of type Fixie.Internal.PipeMessage+TestFailed was unexpectedly null."); - } - - static void Expect(TMessage message, string expectedJson) - { - PipeMessage.Serialize(PipeMessage.Deserialize(expectedJson)).ShouldBe(expectedJson); - PipeMessage.Serialize(message).ShouldBe(expectedJson); - } -} \ No newline at end of file diff --git a/src/Fixie.Tests/TestAdapter/VsDiscoveryRecorderTests.cs b/src/Fixie.Tests/TestAdapter/VsDiscoveryRecorderTests.cs deleted file mode 100644 index 05934aab..00000000 --- a/src/Fixie.Tests/TestAdapter/VsDiscoveryRecorderTests.cs +++ /dev/null @@ -1,95 +0,0 @@ -using Fixie.Internal; -using Fixie.TestAdapter; -using Microsoft.VisualStudio.TestPlatform.ObjectModel; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; -using Fixie.Tests.Reports; - -namespace Fixie.Tests.TestAdapter; - -public class VsDiscoveryRecorderTests : MessagingTests -{ - public void ShouldMapDiscoveredTestsToVsTestDiscoverySink() - { - var assemblyPath = typeof(MessagingTests).Assembly.Location; - - var log = new StubMessageLogger(); - var discoverySink = new StubTestCaseDiscoverySink(); - - var vsDiscoveryRecorder = new VsDiscoveryRecorder(discoverySink, assemblyPath); - - RecordAnticipatedPipeMessages(assemblyPath, vsDiscoveryRecorder); - - log.Messages.ShouldMatch([]); - - discoverySink.TestCases.ItemsShouldSatisfy([ - x => x.ShouldBeDiscoveryTimeTest(TestClass + ".Fail", assemblyPath), - x => x.ShouldBeDiscoveryTimeTest(TestClass + ".FailByAssertion", assemblyPath), - x => x.ShouldBeDiscoveryTimeTestMissingSourceLocation(TestClass + ".Pass", assemblyPath), - x => x.ShouldBeDiscoveryTimeTest(TestClass + ".Skip", assemblyPath), - x => x.ShouldBeDiscoveryTimeTest(GenericTestClass + ".ShouldBeString", assemblyPath) - ]); - } - - void RecordAnticipatedPipeMessages(string assemblyPath, VsDiscoveryRecorder vsDiscoveryRecorder) - { - SourceLocationProvider sourceLocationProvider = new(assemblyPath); - - var test = TestClass + ".Fail"; - sourceLocationProvider.TryGetSourceLocation(test, out var sourceLocation).ShouldBe(true); - vsDiscoveryRecorder.Record(new PipeMessage.TestDiscovered - { - Test = test, - SourceLocation = sourceLocation - }); - - test = TestClass + ".FailByAssertion"; - sourceLocationProvider.TryGetSourceLocation(test, out sourceLocation).ShouldBe(true); - vsDiscoveryRecorder.Record(new PipeMessage.TestDiscovered - { - Test = test, - SourceLocation = sourceLocation - }); - - test = TestClass + ".Pass"; - sourceLocationProvider.TryGetSourceLocation(test, out sourceLocation).ShouldBe(false); - sourceLocation.ShouldBe(null); - vsDiscoveryRecorder.Record(new PipeMessage.TestDiscovered - { - Test = test, - SourceLocation = sourceLocation - }); - - test = TestClass + ".Skip"; - sourceLocationProvider.TryGetSourceLocation(test, out sourceLocation).ShouldBe(true); - vsDiscoveryRecorder.Record(new PipeMessage.TestDiscovered - { - Test = test, - SourceLocation = sourceLocation - }); - - test = GenericTestClass + ".ShouldBeString"; - sourceLocationProvider.TryGetSourceLocation(test, out sourceLocation).ShouldBe(true); - vsDiscoveryRecorder.Record(new PipeMessage.TestDiscovered - { - Test = test, - SourceLocation = sourceLocation - }); - } - - class StubMessageLogger : IMessageLogger - { - public List Messages { get; } = []; - - public void SendMessage(TestMessageLevel testMessageLevel, string message) - => Messages.Add($"{testMessageLevel}: {message}"); - } - - class StubTestCaseDiscoverySink : ITestCaseDiscoverySink - { - public List TestCases { get; } = []; - - public void SendTestCase(TestCase discoveredTest) - => TestCases.Add(discoveredTest); - } -} \ No newline at end of file diff --git a/src/Fixie.Tests/TestAdapter/VsExecutionRecorderTests.cs b/src/Fixie.Tests/TestAdapter/VsExecutionRecorderTests.cs deleted file mode 100644 index b0311066..00000000 --- a/src/Fixie.Tests/TestAdapter/VsExecutionRecorderTests.cs +++ /dev/null @@ -1,270 +0,0 @@ -using Fixie.TestAdapter; -using Microsoft.VisualStudio.TestPlatform.ObjectModel; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; -using Fixie.Internal; -using Fixie.Tests.Reports; -using static System.Environment; - -namespace Fixie.Tests.TestAdapter; - -using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult; - -public class VsExecutionRecorderTests : MessagingTests -{ - public void ShouldMapMessagesToVsTestExecutionRecorder() - { - const string assemblyPath = "assembly.path.dll"; - var recorder = new StubExecutionRecorder(); - - var vsExecutionRecorder = new VsExecutionRecorder(recorder, assemblyPath); - - RecordAnticipatedPipeMessages(vsExecutionRecorder); - - var messages = recorder.Messages; - - messages.Count.ShouldBe(13); - - foreach (var message in messages) - { - if (message is TestResult result) - { - result.Traits.ToArray().ShouldMatch([]); - result.Attachments.ShouldMatch([]); - result.ComputerName.ShouldBe(MachineName); - } - } - - var failStart = (TestCase)messages[0]; - var fail = (TestResult)messages[1]; - - var failByAssertionStart = (TestCase)messages[2]; - var failByAssertion = (TestResult)messages[3]; - - var passStart = (TestCase)messages[4]; - var pass = (TestResult)messages[5]; - - var skip = (TestResult)messages[6]; - - var shouldBeStringPassAStart = (TestCase)messages[7]; - var shouldBeStringPassA = (TestResult)messages[8]; - - var shouldBeStringPassBStart = (TestCase)messages[9]; - var shouldBeStringPassB = (TestResult)messages[10]; - - var shouldBeStringFailStart = (TestCase)messages[11]; - var shouldBeStringFail = (TestResult)messages[12]; - - failStart.ShouldBeExecutionTimeTest(TestClass + ".Fail", assemblyPath); - - fail.TestCase.ShouldBeExecutionTimeTest(TestClass+".Fail", assemblyPath); - fail.TestCase.DisplayName.ShouldBe(TestClass+".Fail"); - fail.Outcome.ShouldBe(TestOutcome.Failed); - fail.ErrorMessage.ShouldBe("'Fail' failed!"); - fail.ErrorStackTrace - .ShouldBeStackTrace(["Fixie.Tests.FailureException", At("Fail()")]); - fail.DisplayName.ShouldBe(TestClass+".Fail"); - fail.Messages.ShouldMatch([]); - fail.Duration.ShouldBe(TimeSpan.FromMilliseconds(102)); - - failByAssertionStart.ShouldBeExecutionTimeTest(TestClass + ".FailByAssertion", assemblyPath); - - failByAssertion.TestCase.ShouldBeExecutionTimeTest(TestClass+".FailByAssertion", assemblyPath); - failByAssertion.TestCase.DisplayName.ShouldBe(TestClass+".FailByAssertion"); - failByAssertion.Outcome.ShouldBe(TestOutcome.Failed); - failByAssertion.ErrorMessage.ShouldBe(xShouldBe2ButWas1); - failByAssertion.ErrorStackTrace - .ShouldBeStackTrace(["Fixie.Assertions.AssertException", At("FailByAssertion()")]); - failByAssertion.DisplayName.ShouldBe(TestClass+".FailByAssertion"); - failByAssertion.Messages.ShouldMatch([]); - failByAssertion.Duration.ShouldBe(TimeSpan.FromMilliseconds(103)); - - passStart.ShouldBeExecutionTimeTest(TestClass + ".Pass", assemblyPath); - - pass.TestCase.ShouldBeExecutionTimeTest(TestClass+".Pass", assemblyPath); - pass.TestCase.DisplayName.ShouldBe(TestClass+".Pass"); - pass.Outcome.ShouldBe(TestOutcome.Passed); - pass.ErrorMessage.ShouldBe(null); - pass.ErrorStackTrace.ShouldBe(null); - pass.DisplayName.ShouldBe(TestClass+".Pass"); - pass.Messages.ShouldMatch([]); - pass.Duration.ShouldBe(TimeSpan.FromMilliseconds(104)); - - skip.TestCase.ShouldBeExecutionTimeTest(TestClass+".Skip", assemblyPath); - skip.TestCase.DisplayName.ShouldBe(TestClass+".Skip"); - skip.Outcome.ShouldBe(TestOutcome.Skipped); - skip.ErrorMessage.ShouldBe("⚠ Skipped with attribute."); - skip.ErrorStackTrace.ShouldBe(null); - skip.DisplayName.ShouldBe(TestClass+".Skip"); - skip.Messages.ShouldMatch([]); - skip.Duration.ShouldBe(TimeSpan.Zero); - - shouldBeStringPassAStart.ShouldBeExecutionTimeTest(GenericTestClass + ".ShouldBeString", assemblyPath); - - shouldBeStringPassA.TestCase.ShouldBeExecutionTimeTest(GenericTestClass+".ShouldBeString", assemblyPath); - shouldBeStringPassA.TestCase.DisplayName.ShouldBe(GenericTestClass+".ShouldBeString"); - shouldBeStringPassA.Outcome.ShouldBe(TestOutcome.Passed); - shouldBeStringPassA.ErrorMessage.ShouldBe(null); - shouldBeStringPassA.ErrorStackTrace.ShouldBe(null); - shouldBeStringPassA.DisplayName.ShouldBe(GenericTestClass+".ShouldBeString(\"A\")"); - shouldBeStringPassA.Messages.ShouldMatch([]); - shouldBeStringPassA.Duration.ShouldBe(TimeSpan.FromMilliseconds(105)); - - shouldBeStringPassBStart.ShouldBeExecutionTimeTest(GenericTestClass + ".ShouldBeString", assemblyPath); - - shouldBeStringPassB.TestCase.ShouldBeExecutionTimeTest(GenericTestClass+".ShouldBeString", assemblyPath); - shouldBeStringPassB.TestCase.DisplayName.ShouldBe(GenericTestClass+".ShouldBeString"); - shouldBeStringPassB.Outcome.ShouldBe(TestOutcome.Passed); - shouldBeStringPassB.ErrorMessage.ShouldBe(null); - shouldBeStringPassB.ErrorStackTrace.ShouldBe(null); - shouldBeStringPassB.DisplayName.ShouldBe(GenericTestClass+".ShouldBeString(\"B\")"); - shouldBeStringPassB.Messages.ShouldMatch([]); - shouldBeStringPassB.Duration.ShouldBe(TimeSpan.FromMilliseconds(106)); - - shouldBeStringFailStart.ShouldBeExecutionTimeTest(GenericTestClass + ".ShouldBeString", assemblyPath); - - shouldBeStringFail.TestCase.ShouldBeExecutionTimeTest(GenericTestClass+".ShouldBeString", assemblyPath); - shouldBeStringFail.TestCase.DisplayName.ShouldBe(GenericTestClass+".ShouldBeString"); - shouldBeStringFail.Outcome.ShouldBe(TestOutcome.Failed); - shouldBeStringFail.ErrorMessage.ShouldBe(genericArgumentShouldMatchStringButWasInt); - shouldBeStringFail.ErrorStackTrace - .ShouldBeStackTrace([ - "Fixie.Assertions.AssertException", - At("ShouldBeString[T](T genericArgument)") - ]); - shouldBeStringFail.DisplayName.ShouldBe(GenericTestClass+".ShouldBeString(123)"); - shouldBeStringFail.Messages.ShouldMatch([]); - shouldBeStringFail.Duration.ShouldBe(TimeSpan.FromMilliseconds(107)); - } - - void RecordAnticipatedPipeMessages(VsExecutionRecorder vsExecutionRecorder) - { - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestStarted - { - Test = TestClass + ".Fail" - })); - - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestFailed - { - Test = TestClass + ".Fail", - TestCase = TestClass + ".Fail", - DurationInMilliseconds = 102, - Reason = new PipeMessage.Exception - { - Type = "Fixie.Tests.FailureException", - Message = "'Fail' failed!", - StackTrace = At("Fail()") - } - })); - - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestStarted - { - Test = TestClass + ".FailByAssertion" - })); - - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestFailed - { - Test = TestClass + ".FailByAssertion", - TestCase = TestClass + ".FailByAssertion", - DurationInMilliseconds = 103, - Reason = new PipeMessage.Exception - { - Type = "Fixie.Assertions.AssertException", - Message = xShouldBe2ButWas1, - StackTrace = At("FailByAssertion()") - } - })); - - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestStarted - { - Test = TestClass + ".Pass" - })); - - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestPassed - { - Test = TestClass+".Pass", - TestCase = TestClass+".Pass", - DurationInMilliseconds = 104 - })); - - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestSkipped - { - Test =TestClass+".Skip", - TestCase = TestClass+".Skip", - DurationInMilliseconds = 0, - Reason = "⚠ Skipped with attribute." - })); - - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestStarted - { - Test = GenericTestClass + ".ShouldBeString" - })); - - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestPassed - { - Test = GenericTestClass+".ShouldBeString", - TestCase = GenericTestClass+".ShouldBeString(\"A\")", - DurationInMilliseconds = 105 - })); - - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestStarted - { - Test = GenericTestClass + ".ShouldBeString" - })); - - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestPassed - { - Test = GenericTestClass+".ShouldBeString", - TestCase = GenericTestClass+".ShouldBeString(\"B\")", - DurationInMilliseconds = 106 - })); - - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestStarted - { - Test = GenericTestClass + ".ShouldBeString" - })); - - vsExecutionRecorder.Record(Deserialized(new PipeMessage.TestFailed - { - Test = GenericTestClass+".ShouldBeString", - TestCase = GenericTestClass+".ShouldBeString(123)", - DurationInMilliseconds = 107, - Reason = new PipeMessage.Exception - { - Type = "Fixie.Assertions.AssertException", - Message = genericArgumentShouldMatchStringButWasInt, - StackTrace = At("ShouldBeString[T](T genericArgument)") - } - })); - } - - static T Deserialized(T original) - { - // Because the inter-process communication between the VsTest process - // and the test assembly process is not exercised in these single-process - // tests, put a given sample message through the same serialization round - // trip that would be applied at runtime, in order to detect data loss. - - return PipeMessage.Deserialize(PipeMessage.Serialize(original)); - } - - class StubExecutionRecorder : ITestExecutionRecorder - { - public List Messages { get; } = []; - - public void RecordStart(TestCase testCase) - => Messages.Add(testCase); - - public void RecordResult(TestResult testResult) - => Messages.Add(testResult); - - public void SendMessage(TestMessageLevel testMessageLevel, string message) - => throw new NotImplementedException(); - - public void RecordEnd(TestCase testCase, TestOutcome outcome) - => throw new NotImplementedException(); - - public void RecordAttachments(IList attachmentSets) - => throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/src/Fixie/Internal/EntryPoint.cs b/src/Fixie/Internal/EntryPoint.cs index 1427c68e..7262d998 100644 --- a/src/Fixie/Internal/EntryPoint.cs +++ b/src/Fixie/Internal/EntryPoint.cs @@ -1,5 +1,4 @@ -using System.IO.Pipes; -using System.Reflection; +using System.Reflection; using Fixie.Reports; using static System.Environment; using static Fixie.Internal.Maybe; @@ -25,64 +24,13 @@ public static async Task Main(Assembly assembly, string[] customArguments) try { - var pipeName = GetEnvironmentVariable("FIXIE_NAMED_PIPE"); + var reports = DefaultReports(environment).ToArray(); - if (pipeName == null) - { - var reports = DefaultReports(environment).ToArray(); + var pattern = GetEnvironmentVariable("FIXIE_TESTS_PATTERN"); - var pattern = GetEnvironmentVariable("FIXIE_TESTS_PATTERN"); - - return pattern == null - ? (int) await Run(environment, reports, async runner => await runner.Run()) - : (int) await Run(environment, reports, async runner => await runner.Run(new TestPattern(pattern))); - } - - using var pipeStream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut); - using var pipe = new TestAdapterPipe(pipeStream); - - pipeStream.Connect(); - pipeStream.ReadMode = PipeTransmissionMode.Byte; - - var testAdapterReport = new TestAdapterReport(environment, pipe); - - var exitCode = ExitCode.Success; - - try - { - var messageType = pipe.ReceiveMessageType(); - - if (messageType == typeof(PipeMessage.DiscoverTests).FullName) - { - var discoverTests = pipe.Receive(); - await DiscoverMethods(environment, testAdapterReport); - } - else if (messageType == typeof(PipeMessage.ExecuteTests).FullName) - { - var executeTests = pipe.Receive(); - - var reports = new IReport[] { testAdapterReport }; - - exitCode = executeTests.Filter.Length == 0 - ? await Run(environment, reports, async runner => await runner.Run()) - : await Run(environment, reports, async runner => await runner.Run([..executeTests.Filter])); - } - else - { - var body = pipe.ReceiveMessageBody(); - throw new Exception($"Test assembly received unexpected message of type {messageType}: {body}"); - } - } - catch (Exception exception) - { - pipe.Send(exception); - } - finally - { - pipe.Send(); - } - - return (int)exitCode; + return pattern == null + ? (int) await Run(environment, reports, async runner => await runner.Run()) + : (int) await Run(environment, reports, async runner => await runner.Run(new TestPattern(pattern))); } catch (Exception exception) { @@ -93,12 +41,6 @@ public static async Task Main(Assembly assembly, string[] customArguments) } } - static async Task DiscoverMethods(TestEnvironment environment, TestAdapterReport testAdapterReport) - { - var runner = new Runner(environment, testAdapterReport); - await runner.Discover(); - } - static async Task Run(TestEnvironment environment, IReport[] reports, Func> run) { var runner = new Runner(environment, reports); diff --git a/src/Fixie/Internal/PipeMessage.cs b/src/Fixie/Internal/PipeMessage.cs deleted file mode 100644 index ca3ce0d2..00000000 --- a/src/Fixie/Internal/PipeMessage.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json; -using Fixie.Reports; - -namespace Fixie.Internal; - -static class PipeMessage -{ - public static string Serialize(TMessage message) - { - return JsonSerializer.Serialize(message); - } - - public static TMessage Deserialize(string json) - { - var message = JsonSerializer.Deserialize(json); - - if (message == null) - throw new System.Exception($"Message of type {typeof(TMessage).FullName} was unexpectedly null."); - - return message; - } - - public class DiscoverTests { } - - public class ExecuteTests - { - public required string[] Filter { get; init; } - } - - public class TestDiscovered - { - public required string Test { get; init; } - public required SourceLocation? SourceLocation { get; init; } - } - - public class TestStarted - { - public required string Test { get; init; } - } - - public abstract class TestCompleted - { - public required string Test { get; init; } - public required string TestCase { get; init; } - public required double DurationInMilliseconds { get; init; } - } - - public class TestSkipped : TestCompleted - { - public required string Reason { get; init; } - } - - public class TestPassed : TestCompleted - { - } - - public class TestFailed : TestCompleted - { - public required Exception Reason { get; init; } - } - - public class Exception - { - public Exception() - { - } - - [SetsRequiredMembers] - public Exception(System.Exception exception) - { - Type = exception.GetType().FullName!; - Message = exception.Message; - StackTrace = exception.StackTraceSummary(); - } - - public required string Type { get; init; } - public required string Message { get; init; } - public required string StackTrace { get; init; } - } - - public class EndOfPipe { } -} \ No newline at end of file diff --git a/src/Fixie/Internal/TestAdapterPipe.cs b/src/Fixie/Internal/TestAdapterPipe.cs deleted file mode 100644 index 7ff5b168..00000000 --- a/src/Fixie/Internal/TestAdapterPipe.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.IO.Pipes; -using System.Text; - -namespace Fixie.Internal; - -class TestAdapterPipe : IDisposable -{ - readonly StreamReader reader; - readonly StreamWriter writer; - - public TestAdapterPipe(PipeStream pipeStream) - { - // Normal attempts to call dispose on this reader - // and writer cause the underlying PipeStream to - // throw ObjectDisposedException when the original - // PipeStream's 'using' block ends. Here we allow - // the reader and writer to be disposed of, ignoring - // the PipeStream itself by leaving that open. - // Then, the original using block for the PipeStream - // is trusted to own that cleanup. - - reader = new StreamReader(pipeStream, leaveOpen: true); - writer = new StreamWriter(pipeStream, leaveOpen: true); - } - - const string EndOfMessage = "--End of Message--"; - - public string? ReceiveMessageType() - { - return reader.ReadLine(); - } - - public TMessage Receive() - { - return PipeMessage.Deserialize(ReceiveMessageBody()); - } - - public string ReceiveMessageBody() - { - var lines = new StringBuilder(); - - while (true) - { - var line = reader.ReadLine(); - - if (line == null || line == EndOfMessage) - break; - - lines.AppendLine(line); - } - - return lines.ToString(); - } - - public void Send() where TMessage: new() - { - Send(new TMessage()); - } - - public void Send(Exception exception) - { - Send(new PipeMessage.Exception(exception)); - } - - public void Send(TMessage message) - { - var messageType = typeof(TMessage).FullName!; - - writer.WriteLine(messageType); - writer.WriteLine(PipeMessage.Serialize(message)); - writer.WriteLine(EndOfMessage); - writer.Flush(); - } - - public void Dispose() - { - reader.Dispose(); - writer.Dispose(); - } -} \ No newline at end of file diff --git a/src/Fixie/Reports/TestAdapterReport.cs b/src/Fixie/Reports/TestAdapterReport.cs deleted file mode 100644 index 375629fd..00000000 --- a/src/Fixie/Reports/TestAdapterReport.cs +++ /dev/null @@ -1,89 +0,0 @@ -using Fixie.Internal; - -namespace Fixie.Reports; - -class TestAdapterReport(TestEnvironment environment, TestAdapterPipe pipe) : - IHandler, - IHandler, - IHandler, - IHandler, - IHandler -{ - readonly SourceLocationProvider sourceLocationProvider = new(environment.Assembly.Location); - - public Task Handle(TestDiscovered message) - { - SourceLocation? sourceLocation = null; - - try - { - sourceLocationProvider.TryGetSourceLocation(message.Test, out sourceLocation); - } - catch (Exception exception) - { - using (Foreground.Yellow) - environment.Console.WriteLine( - $"{GetType().FullName} threw an exception while " + - $"attempting to handle a message of type {typeof(TestDiscovered).FullName}:"); - environment.Console.WriteLine(); - environment.Console.WriteLine(exception.ToString()); - environment.Console.WriteLine(); - } - - pipe.Send(new PipeMessage.TestDiscovered - { - Test = message.Test, - SourceLocation = sourceLocation - }); - - return Task.CompletedTask; - } - - public Task Handle(TestStarted message) - { - pipe.Send(new PipeMessage.TestStarted - { - Test = message.Test - }); - - return Task.CompletedTask; - } - - public Task Handle(TestSkipped message) - { - pipe.Send(new PipeMessage.TestSkipped - { - Test = message.Test, - TestCase = message.TestCase, - DurationInMilliseconds = message.Duration.TotalMilliseconds, - Reason = message.Reason - }); - - return Task.CompletedTask; - } - - public Task Handle(TestPassed message) - { - pipe.Send(new PipeMessage.TestPassed - { - Test = message.Test, - TestCase = message.TestCase, - DurationInMilliseconds = message.Duration.TotalMilliseconds - }); - - return Task.CompletedTask; - } - - public Task Handle(TestFailed message) - { - pipe.Send(new PipeMessage.TestFailed - { - Test = message.Test, - TestCase = message.TestCase, - DurationInMilliseconds = message.Duration.TotalMilliseconds, - Reason = new PipeMessage.Exception(message.Reason) - }); - - return Task.CompletedTask; - } -} \ No newline at end of file From 65609915492080afd18069440d4f9442645810e2 Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Thu, 17 Apr 2025 20:33:28 -0500 Subject: [PATCH 17/19] Drop unreachable code from the VSTest adapter. --- .../DebuggerAttachmentFailure.cs | 90 ---------- src/Fixie.TestAdapter/InternalsVisibleTo.cs | 3 - src/Fixie.TestAdapter/LoggingExtensions.cs | 19 --- src/Fixie.TestAdapter/RunnerException.cs | 9 - src/Fixie.TestAdapter/TestAssembly.cs | 160 ------------------ .../TestProcessExitException.cs | 27 --- src/Fixie.TestAdapter/VsTestDiscoverer.cs | 23 --- src/Fixie.TestAdapter/VsTestExecutor.cs | 51 +----- .../TestAdapter/TestCaseMappingAssertions.cs | 60 ------- src/Fixie/InternalsVisibleTo.cs | 3 +- 10 files changed, 3 insertions(+), 442 deletions(-) delete mode 100644 src/Fixie.TestAdapter/DebuggerAttachmentFailure.cs delete mode 100644 src/Fixie.TestAdapter/InternalsVisibleTo.cs delete mode 100644 src/Fixie.TestAdapter/LoggingExtensions.cs delete mode 100644 src/Fixie.TestAdapter/RunnerException.cs delete mode 100644 src/Fixie.TestAdapter/TestAssembly.cs delete mode 100644 src/Fixie.TestAdapter/TestProcessExitException.cs delete mode 100644 src/Fixie.Tests/TestAdapter/TestCaseMappingAssertions.cs diff --git a/src/Fixie.TestAdapter/DebuggerAttachmentFailure.cs b/src/Fixie.TestAdapter/DebuggerAttachmentFailure.cs deleted file mode 100644 index 8f69dd48..00000000 --- a/src/Fixie.TestAdapter/DebuggerAttachmentFailure.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Fixie.TestAdapter; - -class DebuggerAttachmentFailure -{ - public string Message { get; } - public Exception ThirdPartyTestHostException { get; } - - public DebuggerAttachmentFailure(Exception thirdPartyTestHostException) - { - ThirdPartyTestHostException = thirdPartyTestHostException; - Message = UserGuidanceMessage(thirdPartyTestHostException); - } - - static string UserGuidanceMessage(Exception thirdPartyTestHostException) - { - var host = TryGetTestHost(out var testHost) - ? $"{Environment.NewLine}{testHost}{Environment.NewLine}" - : ""; - - return $@"Fixie attempted to run your test assembly -under the active debugger session, but the -third-party test host {host}failed to honor the request to attach to the -test assembly process. The run continued -without the debugger so that this message -could be reported. - - -In order to debug your test, bypass your -test runner's unimplemented ""Debug"" -option using one of the following two -approaches. - -If your operating system supports -Debugger.Launch(): - - 1. Add the following line at the start - of your test: - - System.Diagnostics.Debugger.Launch(); - - 2. Use your test runner's ""Run"" option - to start the test instead of using - its unimplemented ""Debug"" option. - - 3. Make a selection in the resulting - debugger session dialog. - - -If your operating system does not support -Debugger.Launch(): - - 1. Note that Fixie test projects are in - fact normal console applications that - run their own tests. - - 2. Instead of using your test runner, - run your test project under your - development environment's debugger - as a normal console application. - - -When filing a bug report to your specific -third-party test runner's organization, -please include the following exception -thrown by their implementation of the VSTest -API. Test runners are meant to implement -IFrameworkHandle.LaunchProcessWithDebuggerAttached -since they have control over the active -debugger session. - - -{thirdPartyTestHostException.Message}"; - } - - static bool TryGetTestHost([NotNullWhen(true)] out string? testHost) - { - try - { - testHost = Environment.GetCommandLineArgs().First(); - } - catch - { - testHost = null; - } - - return testHost != null; - } -} \ No newline at end of file diff --git a/src/Fixie.TestAdapter/InternalsVisibleTo.cs b/src/Fixie.TestAdapter/InternalsVisibleTo.cs deleted file mode 100644 index f5102558..00000000 --- a/src/Fixie.TestAdapter/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly:InternalsVisibleTo("Fixie.Tests")] \ No newline at end of file diff --git a/src/Fixie.TestAdapter/LoggingExtensions.cs b/src/Fixie.TestAdapter/LoggingExtensions.cs deleted file mode 100644 index ca70cd3b..00000000 --- a/src/Fixie.TestAdapter/LoggingExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Fixie.Internal; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; - -namespace Fixie.TestAdapter; - -static class LoggingExtensions -{ - public static void Info(this IMessageLogger logger, string message) - => logger.SendMessage(TestMessageLevel.Informational, message); - - public static void Error(this IMessageLogger logger, string message) - => logger.SendMessage(TestMessageLevel.Error, message); - - public static void Error(this IMessageLogger logger, Exception exception) - => logger.SendMessage(TestMessageLevel.Error, exception.ToString()); - - public static void Version(this IMessageLogger logger) - => logger.Info(Framework.Version); -} \ No newline at end of file diff --git a/src/Fixie.TestAdapter/RunnerException.cs b/src/Fixie.TestAdapter/RunnerException.cs deleted file mode 100644 index d409d1f9..00000000 --- a/src/Fixie.TestAdapter/RunnerException.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Fixie.TestAdapter; - -class RunnerException : Exception -{ - public RunnerException(Exception exception) - : base(exception.ToString()) - { - } -} \ No newline at end of file diff --git a/src/Fixie.TestAdapter/TestAssembly.cs b/src/Fixie.TestAdapter/TestAssembly.cs deleted file mode 100644 index 4253711d..00000000 --- a/src/Fixie.TestAdapter/TestAssembly.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Text.RegularExpressions; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; - -namespace Fixie.TestAdapter; - -static class TestAssembly -{ - public static bool IsTestAssembly(string assemblyPath) - { - string[] fixieAssemblies = - [ - "Fixie.dll", "Fixie.TestAdapter.dll" - ]; - - if (fixieAssemblies.Contains(Path.GetFileName(assemblyPath))) - return false; - - var folderPath = new FileInfo(assemblyPath).Directory!.FullName; - - return File.Exists(Path.Combine(folderPath, "Fixie.dll")); - } - - public static int? TryGetExitCode(this Process? process) - { - if (process != null && process.WaitForExit(5000)) - return process.ExitCode; - - return null; - } - - public static Process StartDiscovery(string assemblyPath) - { - return Run(assemblyPath); - } - - public static Process? StartExecution(string assemblyPath, IFrameworkHandle frameworkHandle, - out DebuggerAttachmentFailure? attachmentFailure) - { - attachmentFailure = null; - - if (Debugger.IsAttached) - return RunAttemptingDebuggerAttachment(assemblyPath, frameworkHandle, out attachmentFailure); - - return Run(assemblyPath); - } - - static Process Run(string assemblyPath) - { - return Start(new ProcessStartInfo("dotnet", [assemblyPath]) - { - WorkingDirectory = WorkingDirectory(assemblyPath), - UseShellExecute = false - }); - } - - static Process? RunAttemptingDebuggerAttachment(string assemblyPath, IFrameworkHandle frameworkHandle, - out DebuggerAttachmentFailure? attachmentFailure) - { - attachmentFailure = null; - - // LaunchProcessWithDebuggerAttached sends a request back - // to the third-party test runner process which started - // this TestAdapter's host process. That test runner - // process does not know about environment variables - // created so far by this TestAdapter. That test runner - // cannot reliably resolve the meaning of bare commands - // like `dotnet` to the full file path of the corresponding - // executable. To ensure the test runner process can - // successfully honor the request, we must explicitly - // pass along new environment variables and resolve the - // full path for the `dotnet` executable. - - var environmentVariables = new Dictionary - { - ["FIXIE_NAMED_PIPE"] = Environment.GetEnvironmentVariable("FIXIE_NAMED_PIPE") - }; - - var filePath = FindDotnet(); - - try - { - frameworkHandle - .LaunchProcessWithDebuggerAttached( - filePath, - WorkingDirectory(assemblyPath), - Serialize([assemblyPath]), - environmentVariables); - - return null; - } - catch (Exception thirdPartyTestHostException) - { - attachmentFailure = new DebuggerAttachmentFailure(thirdPartyTestHostException); - - frameworkHandle.Error(thirdPartyTestHostException); - - return Run(assemblyPath); - } - } - - static string WorkingDirectory(string assemblyPath) - { - return Path.GetDirectoryName(Path.GetFullPath(assemblyPath))!; - } - - static Process Start(ProcessStartInfo startInfo) - { - var process = new Process - { - StartInfo = startInfo - }; - - if (process.Start()) - return process; - - throw new Exception("Failed to start process: " + startInfo.FileName); - } - - static string FindDotnet() - { - var fileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dotnet.exe" : "dotnet"; - - var folderPath = Environment - .GetEnvironmentVariable("PATH")? - .Split(Path.PathSeparator) - .FirstOrDefault(path => File.Exists(Path.Combine(path.Trim(), fileName))); - - if (folderPath == null) - throw new Exception( - $"Could not locate {fileName} when searching the PATH environment variable. " + - "Verify that you have installed the .NET SDK."); - - return Path.Combine(folderPath.Trim(), fileName); - } - - /// - /// Serialize the given string[] to a single string, so that when used as a ProcessStartInfo.Arguments - /// value, the process's Main method will receive the original string[]. - /// - /// See https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ - /// See https://stackoverflow.com/a/6040946 for the regex approach used here. - /// - public static string Serialize(string[] arguments) - => string.Join(" ", arguments.Select(Quote)); - - static string Quote(string argument) - { - //For each substring of zero or more \ followed by " - //replace it with twice as many \ followed by \" - var s = Regex.Replace(argument, @"(\\*)" + '"', @"$1$1\" + '"'); - - //When an argument ends in \ double the number of \ at the end. - s = Regex.Replace(s, @"(\\+)$", @"$1$1"); - - //Now that the content has been escaped, surround the value in quotes. - return '"' + s + '"'; - } -} \ No newline at end of file diff --git a/src/Fixie.TestAdapter/TestProcessExitException.cs b/src/Fixie.TestAdapter/TestProcessExitException.cs deleted file mode 100644 index 80c8ca12..00000000 --- a/src/Fixie.TestAdapter/TestProcessExitException.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Fixie.TestAdapter; - -public class TestProcessExitException : Exception -{ - const int StackOverflowExitCode = -1073741571; - - public TestProcessExitException(int? exitCode) - : base(GetMessage(exitCode)) - { - } - - static string GetMessage(int? exitCode) - { - const string exitedUnexpectedly = "The test assembly process exited unexpectedly"; - - if (exitCode != null) - { - var withExitCode = $"with exit code {exitCode}"; - - return exitCode == StackOverflowExitCode - ? $"{exitedUnexpectedly} {withExitCode}, indicating the test threw a StackOverflowException." - : $"{exitedUnexpectedly} {withExitCode}."; - } - - return $"{exitedUnexpectedly}."; - } -} \ No newline at end of file diff --git a/src/Fixie.TestAdapter/VsTestDiscoverer.cs b/src/Fixie.TestAdapter/VsTestDiscoverer.cs index 417ee133..d55c2a98 100644 --- a/src/Fixie.TestAdapter/VsTestDiscoverer.cs +++ b/src/Fixie.TestAdapter/VsTestDiscoverer.cs @@ -1,7 +1,6 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; -using static Fixie.TestAdapter.TestAssembly; namespace Fixie.TestAdapter; @@ -12,27 +11,5 @@ class VsTestDiscoverer : ITestDiscoverer { public void DiscoverTests(IEnumerable sources, IDiscoveryContext discoveryContext, IMessageLogger log, ITestCaseDiscoverySink discoverySink) { - try - { - log.Version(); - - foreach (var assemblyPath in sources) - DiscoverTests(log, discoverySink, assemblyPath); - } - catch (Exception exception) - { - throw new RunnerException(exception); - } - } - - static void DiscoverTests(IMessageLogger log, ITestCaseDiscoverySink discoverySink, string assemblyPath) - { - if (!IsTestAssembly(assemblyPath)) - { - log.Info("Skipping " + assemblyPath + " because it is not a test assembly."); - return; - } - - log.Info("Processing " + assemblyPath); } } \ No newline at end of file diff --git a/src/Fixie.TestAdapter/VsTestExecutor.cs b/src/Fixie.TestAdapter/VsTestExecutor.cs index 6fd113cf..90b5048e 100644 --- a/src/Fixie.TestAdapter/VsTestExecutor.cs +++ b/src/Fixie.TestAdapter/VsTestExecutor.cs @@ -1,7 +1,5 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; -using static Fixie.TestAdapter.TestAssembly; namespace Fixie.TestAdapter; @@ -27,21 +25,7 @@ public void RunTests(IEnumerable? sources, IRunContext? runContext, IFra ArgumentNullException.ThrowIfNull(sources); ArgumentNullException.ThrowIfNull(frameworkHandle); - try - { - IMessageLogger log = frameworkHandle; - - log.Version(); - - HandlePoorVsTestImplementationDetails(runContext, frameworkHandle); - - foreach (var assemblyPath in sources) - RunTests(log, frameworkHandle, assemblyPath); - } - catch (Exception exception) - { - throw new RunnerException(exception); - } + HandlePoorVsTestImplementationDetails(runContext, frameworkHandle); } /// @@ -59,42 +43,11 @@ public void RunTests(IEnumerable? tests, IRunContext? runContext, IFra ArgumentNullException.ThrowIfNull(tests); ArgumentNullException.ThrowIfNull(frameworkHandle); - try - { - IMessageLogger log = frameworkHandle; - - log.Version(); - - HandlePoorVsTestImplementationDetails(runContext, frameworkHandle); - - var assemblyGroups = tests.GroupBy(tc => tc.Source); - - foreach (var assemblyGroup in assemblyGroups) - { - var assemblyPath = assemblyGroup.Key; - - RunTests(log, frameworkHandle, assemblyPath); - } - } - catch (Exception exception) - { - throw new RunnerException(exception); - } + HandlePoorVsTestImplementationDetails(runContext, frameworkHandle); } public void Cancel() { } - static void RunTests(IMessageLogger log, IFrameworkHandle frameworkHandle, string assemblyPath) - { - if (!IsTestAssembly(assemblyPath)) - { - log.Info("Skipping " + assemblyPath + " because it is not a test assembly."); - return; - } - - log.Info("Processing " + assemblyPath); - } - static void HandlePoorVsTestImplementationDetails(IRunContext? runContext, IFrameworkHandle frameworkHandle) { if (runContext?.KeepAlive == true) diff --git a/src/Fixie.Tests/TestAdapter/TestCaseMappingAssertions.cs b/src/Fixie.Tests/TestAdapter/TestCaseMappingAssertions.cs deleted file mode 100644 index f0bf8689..00000000 --- a/src/Fixie.Tests/TestAdapter/TestCaseMappingAssertions.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Microsoft.VisualStudio.TestPlatform.ObjectModel; - -namespace Fixie.Tests.TestAdapter; - -public static class TestCaseMappingAssertions -{ - public static void ShouldBeDiscoveryTimeTest(this TestCase test, string expectedFullyQualifiedName, string expectedSource) - { - ShouldHaveIdentity(test, expectedFullyQualifiedName, expectedSource); - - ShouldUseDefaultsForUnmappedProperties(test); - - ShouldHaveSourceLocation(test); - } - - public static void ShouldBeDiscoveryTimeTestMissingSourceLocation(this TestCase test, string expectedFullyQualifiedName, string expectedSource) - { - ShouldHaveIdentity(test, expectedFullyQualifiedName, expectedSource); - - ShouldUseDefaultsForUnmappedProperties(test); - - ShouldNotHaveSourceLocation(test); - } - - public static void ShouldBeExecutionTimeTest(this TestCase test, string expectedFullyQualifiedName, string expectedSource) - { - ShouldHaveIdentity(test, expectedFullyQualifiedName, expectedSource); - - ShouldUseDefaultsForUnmappedProperties(test); - - //Source locations are a discovery-time concern. - ShouldNotHaveSourceLocation(test); - } - - static void ShouldHaveIdentity(TestCase test, string expectedFullyQualifiedName, string expectedSource) - { - test.FullyQualifiedName.ShouldBe(expectedFullyQualifiedName); - test.DisplayName.ShouldBe(test.FullyQualifiedName); - test.Source.ShouldBe(expectedSource); - } - - static void ShouldUseDefaultsForUnmappedProperties(TestCase test) - { - test.Traits.ToArray().ShouldMatch([]); - test.ExecutorUri.ToString().ShouldBe("executor://fixie.testadapter/"); - } - - static void ShouldHaveSourceLocation(TestCase test) - { - test.CodeFilePath.ShouldNotBeNull(); - test.CodeFilePath.EndsWith("MessagingTests.cs").ShouldBe(true); - test.LineNumber.ShouldSatisfy(x => x > 0); - } - - static void ShouldNotHaveSourceLocation(TestCase test) - { - test.CodeFilePath.ShouldBe(null); - test.LineNumber.ShouldBe(-1); - } -} \ No newline at end of file diff --git a/src/Fixie/InternalsVisibleTo.cs b/src/Fixie/InternalsVisibleTo.cs index 0cc608e5..f5102558 100644 --- a/src/Fixie/InternalsVisibleTo.cs +++ b/src/Fixie/InternalsVisibleTo.cs @@ -1,4 +1,3 @@ using System.Runtime.CompilerServices; -[assembly:InternalsVisibleTo("Fixie.Tests")] -[assembly:InternalsVisibleTo("Fixie.TestAdapter")] \ No newline at end of file +[assembly:InternalsVisibleTo("Fixie.Tests")] \ No newline at end of file From 7f6babc2682571b48b8e65ac1bf81f2e51e34a3c Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Thu, 17 Apr 2025 20:39:26 -0500 Subject: [PATCH 18/19] Phase out deprecated dependency Microsoft.NET.Test.Sdk and associated MSBuild props/targets. --- build/Fixie.props | 9 --- build/Fixie.targets | 1 - src/Directory.Packages.props | 1 - .../Fixie.TestAdapter.csproj | 4 -- src/Fixie.TestAdapter/VsTestDiscoverer.cs | 15 ----- src/Fixie.TestAdapter/VsTestExecutor.cs | 56 ------------------- 6 files changed, 86 deletions(-) delete mode 100644 src/Fixie.TestAdapter/VsTestDiscoverer.cs delete mode 100644 src/Fixie.TestAdapter/VsTestExecutor.cs diff --git a/build/Fixie.props b/build/Fixie.props index 1d320bed..c15dae3f 100644 --- a/build/Fixie.props +++ b/build/Fixie.props @@ -1,13 +1,4 @@  - - true - true - - - - - - \ No newline at end of file diff --git a/build/Fixie.targets b/build/Fixie.targets index b60021b1..8578e15e 100644 --- a/build/Fixie.targets +++ b/build/Fixie.targets @@ -3,7 +3,6 @@ Exe - false diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 3638c728..db2eaebd 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -4,7 +4,6 @@ - diff --git a/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj b/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj index 2e5b4934..3e9f9a49 100644 --- a/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj +++ b/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj @@ -5,10 +5,6 @@ Visual Studio integration for the Fixie test framework. - - - - diff --git a/src/Fixie.TestAdapter/VsTestDiscoverer.cs b/src/Fixie.TestAdapter/VsTestDiscoverer.cs deleted file mode 100644 index d55c2a98..00000000 --- a/src/Fixie.TestAdapter/VsTestDiscoverer.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.VisualStudio.TestPlatform.ObjectModel; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; - -namespace Fixie.TestAdapter; - -[DefaultExecutorUri(VsTestExecutor.Id)] -[FileExtension(".exe")] -[FileExtension(".dll")] -class VsTestDiscoverer : ITestDiscoverer -{ - public void DiscoverTests(IEnumerable sources, IDiscoveryContext discoveryContext, IMessageLogger log, ITestCaseDiscoverySink discoverySink) - { - } -} \ No newline at end of file diff --git a/src/Fixie.TestAdapter/VsTestExecutor.cs b/src/Fixie.TestAdapter/VsTestExecutor.cs deleted file mode 100644 index 90b5048e..00000000 --- a/src/Fixie.TestAdapter/VsTestExecutor.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Microsoft.VisualStudio.TestPlatform.ObjectModel; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; - -namespace Fixie.TestAdapter; - -[ExtensionUri(Id)] -public class VsTestExecutor : ITestExecutor -{ - public const string Id = "executor://fixie.testadapter/"; - public static readonly Uri Uri = new(Id); - - /// - /// This method was originally intended to be called by VsTest when running - /// all tests. However, VsTest no longer appears to call this method, instead - /// favoring its overload with all individual tests specified as if they were - /// all selected by the user. The stated reason is for performance, but in - /// fact requires far larger messages to be passed between processes and far - /// more cross-referencing of test names within each specific test framework - /// at execution time. This overload is maintained optimistically and for - /// protection in the event that VsTest changes back to the more efficient - /// approach. - /// - public void RunTests(IEnumerable? sources, IRunContext? runContext, IFrameworkHandle? frameworkHandle) - { - ArgumentNullException.ThrowIfNull(sources); - ArgumentNullException.ThrowIfNull(frameworkHandle); - - HandlePoorVsTestImplementationDetails(runContext, frameworkHandle); - } - - /// - /// This method was originally intended to be called by VsTest only when running - /// a selected subset of previously discovered tests. However, VsTest appears to - /// call this method even in the event the user is running *all* tests, with all - /// individual tests specified in the input as if all were individually selected - /// by the user. The stated reason is for performance, but in fact requires far - /// larger messages to be passed between processes and far more cross-referencing - /// of test names within each specific test framework at execution time. Still, - /// this overload is functionally correct even when all tests are passed to it. - /// - public void RunTests(IEnumerable? tests, IRunContext? runContext, IFrameworkHandle? frameworkHandle) - { - ArgumentNullException.ThrowIfNull(tests); - ArgumentNullException.ThrowIfNull(frameworkHandle); - - HandlePoorVsTestImplementationDetails(runContext, frameworkHandle); - } - - public void Cancel() { } - - static void HandlePoorVsTestImplementationDetails(IRunContext? runContext, IFrameworkHandle frameworkHandle) - { - if (runContext?.KeepAlive == true) - frameworkHandle.EnableShutdownAfterTestRun = true; - } -} \ No newline at end of file From 1d97f3d86a17647bc10acd44d452bea0cd26c99b Mon Sep 17 00:00:00 2001 From: Patrick Lioi Date: Thu, 17 Apr 2025 20:42:07 -0500 Subject: [PATCH 19/19] Drop deprecated project Fixie.TestAdapter. --- src/Fixie.TestAdapter/Fixie.TestAdapter.csproj | 12 ------------ src/Fixie.Tests/Fixie.Tests.csproj | 1 - src/Fixie.slnx | 1 - 3 files changed, 14 deletions(-) delete mode 100644 src/Fixie.TestAdapter/Fixie.TestAdapter.csproj diff --git a/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj b/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj deleted file mode 100644 index 3e9f9a49..00000000 --- a/src/Fixie.TestAdapter/Fixie.TestAdapter.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - net9.0 - Visual Studio integration for the Fixie test framework. - - - - - - - \ No newline at end of file diff --git a/src/Fixie.Tests/Fixie.Tests.csproj b/src/Fixie.Tests/Fixie.Tests.csproj index 83960efb..360dd718 100644 --- a/src/Fixie.Tests/Fixie.Tests.csproj +++ b/src/Fixie.Tests/Fixie.Tests.csproj @@ -13,7 +13,6 @@ - diff --git a/src/Fixie.slnx b/src/Fixie.slnx index ffb7ab36..dd192d84 100644 --- a/src/Fixie.slnx +++ b/src/Fixie.slnx @@ -5,7 +5,6 @@ -