From 6feadbafd02e26967037cd41c33a0497083a2e5a Mon Sep 17 00:00:00 2001 From: Arun Mahapatra Date: Thu, 3 Sep 2026 18:59:41 +0530 Subject: [PATCH 1/2] feat: decouple vstest and mtp workflows. See #229 --- .config/dotnet-tools.json | 2 +- .github/workflows/dotnet.yml | 15 +- .gitignore | 3 +- Directory.Build.props | 2 +- Directory.Packages.props | 3 +- build.ps1 | 23 +- build.sh | 10 +- src/Directory.Build.props | 28 ++- .../JUnit.Xml.Package.csproj | 22 +- .../JUnitXml.TestLogger.nuspec | 8 +- .../JUnitXml.TestLogger.props | 12 +- src/JUnit.Xml.TestLogger/Assembly.cs | 4 +- .../JUnit.Xml.TestLogger.MTP.csproj | 28 +++ ...roj => JUnit.Xml.TestLogger.VSTest.csproj} | 12 +- src/JUnit.Xml.TestLogger/JUnitTestReporter.cs | 1 + .../JUnitTestReporterExtensions.cs | 1 + .../JUnitXmlTestLogger.cs | 2 +- .../JunitXmlSerializer.cs | 2 - .../NUnit.Xml.Package.csproj | 19 +- .../NUnitXml.TestLogger.nuspec | 8 +- .../NUnitXml.TestLogger.props | 12 +- src/NUnit.Xml.TestLogger/Assembly.cs | 4 +- .../NUnit.Xml.TestLogger.MTP.csproj | 28 +++ ...roj => NUnit.Xml.TestLogger.VSTest.csproj} | 12 +- src/NUnit.Xml.TestLogger/NUnitTestReporter.cs | 1 + .../NUnitTestReporterExtensions.cs | 1 + .../NUnitXmlSerializer.cs | 1 - .../NUnitXmlTestLogger.cs | 2 +- src/TestLogger.MTPv1/TestLogger.MTPv1.csproj | 3 +- src/TestLogger/Assembly.cs | 7 +- .../Core/DefaultLoggerParameterNames.cs | 16 ++ src/TestLogger/Core/ITestRun.cs | 7 +- src/TestLogger/Core/ITestRunBuilder.cs | 6 - src/TestLogger/Core/LoggerConfiguration.cs | 1 - src/TestLogger/Core/TestCaseNameParser.cs | 104 +-------- src/TestLogger/Core/TestMessageInfo.cs | 1 - src/TestLogger/Core/TestMessageLevel.cs | 26 +++ src/TestLogger/Core/TestOutcome.cs | 36 ++++ src/TestLogger/Core/TestResultInfo.cs | 20 +- src/TestLogger/Core/TestResultMessage.cs | 42 ++++ src/TestLogger/Core/TestRun.cs | 15 +- src/TestLogger/Core/TestRunBuilder.cs | 21 -- .../Core/TestRunCompleteWorkflow.cs | 33 +-- src/TestLogger/Core/TestRunMessageWorkflow.cs | 28 --- src/TestLogger/Core/TestRunResultWorkflow.cs | 198 +----------------- src/TestLogger/Core/TestRunStartWorkflow.cs | 19 -- src/TestLogger/Core/Trait.cs | 32 +++ .../Core/MtpTestCaseNameParserExtensions.cs | 78 +++++++ .../Mtp/Core/MtpTestRunMessageWorkflow.cs | 28 +++ .../Mtp/Core/MtpTestRunResultWorkflow.cs | 137 ++++++++++++ src/TestLogger/{ => Mtp}/TestReporter.cs | 9 +- .../TestReporterCommandLineProvider.cs | 2 +- .../{ => Mtp}/Utilities/ArtifactExtensions.cs | 2 +- src/TestLogger/TestLogger.MTP.csproj | 21 ++ ...Logger.csproj => TestLogger.VSTest.csproj} | 8 +- .../Utilities/AttachmentSetExtensions.cs | 42 ---- .../Core/VSTestTestRunBuilderExtensions.cs | 46 ++++ .../Core/VSTestTestRunCompleteWorkflow.cs | 28 +++ .../Core/VSTestTestRunMessageWorkflow.cs | 16 ++ .../Core/VSTestTestRunResultWorkflow.cs | 85 ++++++++ .../VSTest/Core/VSTestTestRunStartWorkflow.cs | 25 +++ .../Extensions/DefaultTestAdapter.cs | 4 +- .../{ => VSTest}/Extensions/ITestAdapter.cs | 5 +- .../Extensions/ITestAdapterFactory.cs | 2 +- .../{ => VSTest}/Extensions/MSTestAdapter.cs | 33 ++- .../Extensions/NUnitTestAdapter.cs | 40 ++-- .../Extensions/TestAdapterFactory.cs | 2 +- .../Extensions/XunitTestAdapter.cs | 55 ++++- src/TestLogger/{ => VSTest}/TestLogger.cs | 9 +- .../Utilities/AttachmentSetExtensions.cs | 65 ++++++ .../Xunit.Xml.Package.csproj | 19 +- .../XunitXml.TestLogger.nuspec | 8 +- .../XunitXml.TestLogger.props | 12 +- src/Xunit.Xml.TestLogger/Assembly.cs | 2 + .../Xunit.Xml.TestLogger.MTP.csproj | 29 +++ ...roj => Xunit.Xml.TestLogger.VSTest.csproj} | 12 +- src/Xunit.Xml.TestLogger/XunitTestReporter.cs | 1 + .../XunitTestReporterExtensions.cs | 1 + .../XunitXmlSerializer.cs | 1 - .../XunitXmlTestLogger.cs | 2 +- ...Unit.Xml.TestLogger.AcceptanceTests.csproj | 12 +- .../JUnitTestLoggerAcceptanceTests.cs | 3 +- ...tTestLoggerFormatOptionsAcceptanceTests.cs | 7 - .../JUnitTestLoggerNetFullAcceptanceTests.cs | 1 - .../JUnitTestLoggerPathTests.cs | 1 - ...estLoggerResultDirectoryAcceptanceTests.cs | 1 - ...toreConsoleOutputOptionsAcceptanceTests.cs | 10 - .../JUnitTestLoggerXUnitAcceptanceTests.cs | 3 +- .../JUnit.Xml.TestLogger.UnitTests.csproj | 12 +- .../JUnitXmlTestSerializerTests.cs | 7 +- .../Json.TestLogger.nuspec | 2 +- .../Json.TestLogger.props | 4 +- .../Json.TestLogger.TestAdapter.csproj | 6 +- test/Json.TestLogger/Json.TestLogger.csproj | 6 +- test/Json.TestLogger/JsonTestLogger.cs | 2 +- ...Unit.Xml.TestLogger.AcceptanceTests.csproj | 7 +- .../NUnitTestLoggerAcceptanceTests.cs | 6 +- .../NUnitTestLoggerNetFullAcceptanceTests.cs | 1 - .../NUnitTestLoggerPathTests.cs | 1 - ...estLoggerResultDirectoryAcceptanceTests.cs | 1 - .../NUnit.Xml.TestLogger.UnitTests.csproj | 12 +- .../TestLogger.AcceptanceTests.csproj | 14 +- .../TestLoggerAcceptanceTests.cs | 7 +- test/TestLogger.Fixtures/DotnetTestFixture.cs | 62 ++++-- .../TestAssetExtensions.cs | 7 +- .../InputSanitizerXmlTests.cs | 0 .../LegacyTestCaseNameParserTests.cs | 0 .../Platform/BaseFileSystemTests.cs | 0 .../Platform/ConsoleOutputTests.cs | 0 .../Platform/FakeFileSystemTests.cs | 0 .../Platform/FileSystemTests.cs | 0 .../TestAttachmentInfoTests.cs | 0 .../TestCaseNameParserTests.cs | 1 + .../TestDoubles/FakeConsoleOutput.cs | 0 .../TestDoubles/FakeFileSystem.cs | 0 .../TestDoubles/InputSanitizerJson.cs | 0 .../TestDoubles/JsonTestResultSerializer.cs | 0 .../TestDoubles/MockCommandLineOptions.cs | 0 .../TestDoubles/MockExtensions.cs | 0 .../TestDoubles/TestableTestReporter.cs | 2 +- .../TestLogger.Mtp.UnitTests.csproj | 36 ++++ .../TestReporterCommandLineProviderTests.cs | 2 +- .../TestReporterTests.cs | 2 +- .../Utilities/ArtifactExtensionsTests.cs | 1 + .../Utilities/StringExtensionsTests.cs | 0 .../TestLogger.PackageTests.csproj | 2 +- .../TestLoggerPackageTests.cs | 2 +- .../TestLogger.UnitTests.csproj | 31 --- .../Builders/TestResultInfoBuilder.cs | 11 +- .../Extensions/MSTestAdapterTests.cs | 14 +- .../Extensions/NUnitTestAdapterTests.cs | 36 ++-- .../Extensions/TestAdapterFactoryTests.cs | 2 +- .../Extensions/XunitTestAdapterTests.cs | 61 +++++- .../LoggerConfigurationTests.cs | 1 + .../TestDoubles/FakeConsoleOutput.cs | 27 +++ .../TestDoubles/FakeFileSystem.cs | 65 ++++++ .../TestDoubles/InputSanitizerJson.cs | 79 +++++++ .../TestDoubles/JsonTestResultSerializer.cs | 170 +++++++++++++++ .../TestDoubles/MockTestLoggerEvents.cs | 0 .../TestDoubles/TestableTestLogger.cs | 1 + .../TestLogger.VSTest.UnitTests.csproj | 35 ++++ .../TestLoggerTests.cs | 3 +- .../TestResultInfoTests.cs | 0 .../TestRunBuilderTests.cs | 17 +- .../TestRunCompleteWorkflowTests.cs | 5 +- .../TestRunMessageWorkflowTests.cs | 4 +- .../TestRunResultWorkflowTests.cs | 5 +- .../TestRunSimulator.cs | 2 +- .../TestRunStartWorkflowTests.cs | 4 +- .../Utilities/AttachmentSetExtensionsTests.cs | 1 + .../AcceptanceTestCollection.cs | 15 ++ .../NoTestSdkAcceptanceTests.cs | 65 ++++++ .../NoTestSdkFixture.cs | 31 +++ .../TestResultsXmlTests.cs | 3 +- .../TestRunFixture.cs | 3 +- ...unit.Xml.TestLogger.AcceptanceTests.csproj | 6 + .../XunitTestLoggerNetFullAcceptanceTests.cs | 1 - test/assets/Directory.Build.props | 22 ++ .../JUnit.Xml.TestLogger.NetCore.Tests.csproj | 6 +- .../JUnit.Xml.TestLogger.NetFull.Tests.csproj | 2 +- ...JUnit.Xml.TestLogger.NetMulti.Tests.csproj | 2 +- ....Xml.TestLogger.XUnit.NetCore.Tests.csproj | 3 +- .../NUnit.Xml.TestLogger.NetCore.Tests.csproj | 6 +- .../NUnit.Xml.TestLogger.NetFull.Tests.csproj | 2 +- ...NUnit.Xml.TestLogger.NetMulti.Tests.csproj | 2 +- .../Xunit.Xml.TestLogger.NetCore.Tests.csproj | 3 +- .../Xunit.Xml.TestLogger.NetFull.Tests.csproj | 2 +- .../UnitTest1.cs | 24 +++ ...unit.Xml.TestLogger.NoTestSdk.Tests.csproj | 32 +++ testlogger.slnx | 15 +- version.txt | 2 +- 171 files changed, 1980 insertions(+), 794 deletions(-) create mode 100644 src/JUnit.Xml.TestLogger/JUnit.Xml.TestLogger.MTP.csproj rename src/JUnit.Xml.TestLogger/{JUnit.Xml.TestLogger.csproj => JUnit.Xml.TestLogger.VSTest.csproj} (58%) create mode 100644 src/NUnit.Xml.TestLogger/NUnit.Xml.TestLogger.MTP.csproj rename src/NUnit.Xml.TestLogger/{NUnit.Xml.TestLogger.csproj => NUnit.Xml.TestLogger.VSTest.csproj} (58%) create mode 100644 src/TestLogger/Core/DefaultLoggerParameterNames.cs create mode 100644 src/TestLogger/Core/TestMessageLevel.cs create mode 100644 src/TestLogger/Core/TestOutcome.cs create mode 100644 src/TestLogger/Core/TestResultMessage.cs create mode 100644 src/TestLogger/Core/Trait.cs create mode 100644 src/TestLogger/Mtp/Core/MtpTestCaseNameParserExtensions.cs create mode 100644 src/TestLogger/Mtp/Core/MtpTestRunMessageWorkflow.cs create mode 100644 src/TestLogger/Mtp/Core/MtpTestRunResultWorkflow.cs rename src/TestLogger/{ => Mtp}/TestReporter.cs (96%) rename src/TestLogger/{ => Mtp}/TestReporterCommandLineProvider.cs (99%) rename src/TestLogger/{ => Mtp}/Utilities/ArtifactExtensions.cs (98%) create mode 100644 src/TestLogger/TestLogger.MTP.csproj rename src/TestLogger/{TestLogger.csproj => TestLogger.VSTest.csproj} (70%) delete mode 100644 src/TestLogger/Utilities/AttachmentSetExtensions.cs create mode 100644 src/TestLogger/VSTest/Core/VSTestTestRunBuilderExtensions.cs create mode 100644 src/TestLogger/VSTest/Core/VSTestTestRunCompleteWorkflow.cs create mode 100644 src/TestLogger/VSTest/Core/VSTestTestRunMessageWorkflow.cs create mode 100644 src/TestLogger/VSTest/Core/VSTestTestRunResultWorkflow.cs create mode 100644 src/TestLogger/VSTest/Core/VSTestTestRunStartWorkflow.cs rename src/TestLogger/{ => VSTest}/Extensions/DefaultTestAdapter.cs (77%) rename src/TestLogger/{ => VSTest}/Extensions/ITestAdapter.cs (74%) rename src/TestLogger/{ => VSTest}/Extensions/ITestAdapterFactory.cs (86%) rename src/TestLogger/{ => VSTest}/Extensions/MSTestAdapter.cs (56%) rename src/TestLogger/{ => VSTest}/Extensions/NUnitTestAdapter.cs (53%) rename src/TestLogger/{ => VSTest}/Extensions/TestAdapterFactory.cs (95%) rename src/TestLogger/{ => VSTest}/Extensions/XunitTestAdapter.cs (53%) rename src/TestLogger/{ => VSTest}/TestLogger.cs (94%) create mode 100644 src/TestLogger/VSTest/Utilities/AttachmentSetExtensions.cs create mode 100644 src/Xunit.Xml.TestLogger/Xunit.Xml.TestLogger.MTP.csproj rename src/Xunit.Xml.TestLogger/{Xunit.Xml.TestLogger.csproj => Xunit.Xml.TestLogger.VSTest.csproj} (60%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/InputSanitizerXmlTests.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/LegacyTestCaseNameParserTests.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/Platform/BaseFileSystemTests.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/Platform/ConsoleOutputTests.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/Platform/FakeFileSystemTests.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/Platform/FileSystemTests.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/TestAttachmentInfoTests.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/TestCaseNameParserTests.cs (99%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/TestDoubles/FakeConsoleOutput.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/TestDoubles/FakeFileSystem.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/TestDoubles/InputSanitizerJson.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/TestDoubles/JsonTestResultSerializer.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/TestDoubles/MockCommandLineOptions.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/TestDoubles/MockExtensions.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/TestDoubles/TestableTestReporter.cs (93%) create mode 100644 test/TestLogger.Mtp.UnitTests/TestLogger.Mtp.UnitTests.csproj rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/TestReporterCommandLineProviderTests.cs (99%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/TestReporterTests.cs (99%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/Utilities/ArtifactExtensionsTests.cs (99%) rename test/{TestLogger.UnitTests => TestLogger.Mtp.UnitTests}/Utilities/StringExtensionsTests.cs (100%) delete mode 100644 test/TestLogger.UnitTests/TestLogger.UnitTests.csproj rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/Builders/TestResultInfoBuilder.cs (91%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/Extensions/MSTestAdapterTests.cs (87%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/Extensions/NUnitTestAdapterTests.cs (81%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/Extensions/TestAdapterFactoryTests.cs (97%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/Extensions/XunitTestAdapterTests.cs (56%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/LoggerConfigurationTests.cs (98%) create mode 100644 test/TestLogger.VSTest.UnitTests/TestDoubles/FakeConsoleOutput.cs create mode 100644 test/TestLogger.VSTest.UnitTests/TestDoubles/FakeFileSystem.cs create mode 100644 test/TestLogger.VSTest.UnitTests/TestDoubles/InputSanitizerJson.cs create mode 100644 test/TestLogger.VSTest.UnitTests/TestDoubles/JsonTestResultSerializer.cs rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/TestDoubles/MockTestLoggerEvents.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/TestDoubles/TestableTestLogger.cs (94%) create mode 100644 test/TestLogger.VSTest.UnitTests/TestLogger.VSTest.UnitTests.csproj rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/TestLoggerTests.cs (98%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/TestResultInfoTests.cs (100%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/TestRunBuilderTests.cs (89%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/TestRunCompleteWorkflowTests.cs (96%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/TestRunMessageWorkflowTests.cs (84%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/TestRunResultWorkflowTests.cs (95%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/TestRunSimulator.cs (98%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/TestRunStartWorkflowTests.cs (96%) rename test/{TestLogger.UnitTests => TestLogger.VSTest.UnitTests}/Utilities/AttachmentSetExtensionsTests.cs (98%) create mode 100644 test/Xunit.Xml.TestLogger.AcceptanceTests/AcceptanceTestCollection.cs create mode 100644 test/Xunit.Xml.TestLogger.AcceptanceTests/NoTestSdkAcceptanceTests.cs create mode 100644 test/Xunit.Xml.TestLogger.AcceptanceTests/NoTestSdkFixture.cs create mode 100644 test/assets/Xunit.Xml.TestLogger.NoTestSdk.Tests/UnitTest1.cs create mode 100644 test/assets/Xunit.Xml.TestLogger.NoTestSdk.Tests/Xunit.Xml.TestLogger.NoTestSdk.Tests.csproj diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 9288b0ed..b1388c01 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-reportgenerator-globaltool": { - "version": "5.5.10", + "version": "5.5.11", "commands": [ "reportgenerator" ] diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 714fc385..d3f95295 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -71,9 +71,10 @@ jobs: run: dotnet pack -c Release -p:PackageVersion=${{ env.APP_BUILD_VERSION }} - name: Unit test run: | - dotnet test -p:PackageVersion=${{ env.APP_BUILD_VERSION }} test/TestLogger.UnitTests/TestLogger.UnitTests.csproj -p:CollectCoverage=true -p:CoverletOutputFormat=opencover - dotnet test -p:PackageVersion=${{ env.APP_BUILD_VERSION }} test/NUnit.Xml.TestLogger.UnitTests/NUnit.Xml.TestLogger.UnitTests.csproj -p:CollectCoverage=true -p:CoverletOutputFormat=opencover - dotnet test -p:PackageVersion=${{ env.APP_BUILD_VERSION }} test/JUnit.Xml.TestLogger.UnitTests/JUnit.Xml.TestLogger.UnitTests.csproj -p:CollectCoverage=true -p:CoverletOutputFormat=opencover + dotnet run -p:PackageVersion=${{ env.APP_BUILD_VERSION }} --project test/TestLogger.VSTest.UnitTests/TestLogger.VSTest.UnitTests.csproj -- --coverlet --coverlet-output-format cobertura --coverlet-include "[Spekt*]*" + dotnet run -p:PackageVersion=${{ env.APP_BUILD_VERSION }} --project test/TestLogger.Mtp.UnitTests/TestLogger.Mtp.UnitTests.csproj -- --coverlet --coverlet-output-format cobertura --coverlet-include "[Spekt*]*" + dotnet run -p:PackageVersion=${{ env.APP_BUILD_VERSION }} --project test/NUnit.Xml.TestLogger.UnitTests/NUnit.Xml.TestLogger.UnitTests.csproj -- --coverlet --coverlet-output-format cobertura --coverlet-include "[Spekt*]*" --coverlet-include "[Microsoft.VisualStudio.TestPlatform.Extension*]*" + dotnet run -p:PackageVersion=${{ env.APP_BUILD_VERSION }} --project test/JUnit.Xml.TestLogger.UnitTests/JUnit.Xml.TestLogger.UnitTests.csproj -- --coverlet --coverlet-output-format cobertura --coverlet-include "[Spekt*]*" --coverlet-include "[Microsoft.VisualStudio.TestPlatform.Extension*]*" - name: Acceptance test run: | echo "Running acceptance tests with verbose output..." @@ -93,16 +94,16 @@ jobs: **/test-results-*.xml **/test-results-*.json **/*.log - test/assets/**/bin/Debug/net8.0/ - test/assets/**/bin/Debug/net10.0/ + test/assets/**/bin/Debug/mtp/ + test/assets/**/bin/Debug/vstest/ - name: Codecov uses: codecov/codecov-action@v3.1.0 with: - files: test/TestLogger.UnitTests/coverage.opencover.xml + files: test/TestLogger.VSTest.UnitTests/bin/Debug/net10.0/TestResults/coverage.cobertura.xml,test/TestLogger.Mtp.UnitTests/bin/Debug/net10.0/TestResults/coverage.cobertura.xml,test/NUnit.Xml.TestLogger.UnitTests/bin/Debug/net10.0/TestResults/coverage.cobertura.xml,test/JUnit.Xml.TestLogger.UnitTests/bin/Debug/net10.0/TestResults/coverage.cobertura.xml - name: Publish packages if: ${{ github.event_name == 'push' && matrix.os != 'windows-latest' }} run: | - dotnet nuget push 'src/TestLogger/bin/Release/*.nupkg' --api-key ${{ secrets.SPEKT_MYGET_KEY }} --source https://www.myget.org/F/spekt/api/v3/index.json + dotnet nuget push 'src/TestLogger/bin/Release/*/*.nupkg' --api-key ${{ secrets.SPEKT_MYGET_KEY }} --source https://www.myget.org/F/spekt/api/v3/index.json dotnet nuget push 'src/Xunit.Xml.Package/bin/Release/*.nupkg' --api-key ${{ secrets.SPEKT_MYGET_KEY }} --source https://www.myget.org/F/spekt/api/v3/index.json dotnet nuget push 'src/NUnit.Xml.Package/bin/Release/*.nupkg' --api-key ${{ secrets.SPEKT_MYGET_KEY }} --source https://www.myget.org/F/spekt/api/v3/index.json dotnet nuget push 'src/JUnit.Xml.Package/bin/Release/*.nupkg' --api-key ${{ secrets.SPEKT_MYGET_KEY }} --source https://www.myget.org/F/spekt/api/v3/index.json diff --git a/.gitignore b/.gitignore index 21bdf5b8..32436ce9 100644 --- a/.gitignore +++ b/.gitignore @@ -141,6 +141,7 @@ _TeamCity* *.coveragexml coverage.json coverage.opencover.xml +coveragereport/ # NCrunch _NCrunch_* @@ -344,4 +345,4 @@ site .vscode/settings.json *.received.txt -global.json \ No newline at end of file +global.json diff --git a/Directory.Build.props b/Directory.Build.props index 67622614..269709e4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ $(MSBuildThisFileDirectory) - 8.0.0 + 8.1.0 diff --git a/Directory.Packages.props b/Directory.Packages.props index 4699c54c..1d8fbb15 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,6 +18,7 @@ - + + diff --git a/build.ps1 b/build.ps1 index 27d9d8fa..afefb00a 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,20 +1,15 @@ -$locals = dotnet nuget locals all -l -ForEach ($local in $($locals -split "`r`n")) -{ - $local = $local.Substring($local.IndexOf(":") + 2) - "Deleting from $local" - dotnet nuget delete Json.TestLogger 4.0.0-dev --force-english-output --non-interactive -s $local -} - -Remove-Item .\test\package\bin\ -Recurse -Remove-Item .\test\package\obj\ -Recurse - dotnet pack if ($?) { - dotnet test test/TestLogger.UnitTests/TestLogger.UnitTests.csproj -p:CollectCoverage=true -p:CoverletOutputFormat=opencover - dotnet test test/NUnit.Xml.TestLogger.UnitTests/NUnit.Xml.TestLogger.UnitTests.csproj -p:CollectCoverage=true -p:CoverletOutputFormat=opencover - dotnet test test/JUnit.Xml.TestLogger.UnitTests/JUnit.Xml.TestLogger.UnitTests.csproj -p:CollectCoverage=true -p:CoverletOutputFormat=opencover + dotnet run --project test/TestLogger.VSTest.UnitTests/TestLogger.VSTest.UnitTests.csproj -- --coverlet --coverlet-output-format cobertura --coverlet-include "[Spekt*]*" + dotnet run --project test/TestLogger.Mtp.UnitTests/TestLogger.Mtp.UnitTests.csproj -- --coverlet --coverlet-output-format cobertura --coverlet-include "[Spekt*]*" + dotnet run --project test/NUnit.Xml.TestLogger.UnitTests/NUnit.Xml.TestLogger.UnitTests.csproj -- --coverlet --coverlet-output-format cobertura --coverlet-include "[Spekt*]*" --coverlet-include "[Microsoft.VisualStudio.TestPlatform.Extension*]*" + dotnet run --project test/JUnit.Xml.TestLogger.UnitTests/JUnit.Xml.TestLogger.UnitTests.csproj -- --coverlet --coverlet-output-format cobertura --coverlet-include "[Spekt*]*" --coverlet-include "[Microsoft.VisualStudio.TestPlatform.Extension*]*" +} +if ($?) { + dotnet tool restore + dotnet reportgenerator -reports:"test/TestLogger.VSTest.UnitTests/bin/Debug/net10.0/TestResults/coverage.cobertura.xml;test/TestLogger.Mtp.UnitTests/bin/Debug/net10.0/TestResults/coverage.cobertura.xml;test/NUnit.Xml.TestLogger.UnitTests/bin/Debug/net10.0/TestResults/coverage.cobertura.xml;test/JUnit.Xml.TestLogger.UnitTests/bin/Debug/net10.0/TestResults/coverage.cobertura.xml" -targetdir:coveragereport -reporttypes:"TextSummary;Html" + Get-Content coveragereport/Summary.txt } if ($?) { dotnet test test/TestLogger.AcceptanceTests/TestLogger.AcceptanceTests.csproj diff --git a/build.sh b/build.sh index 54692235..9fc4cb34 100755 --- a/build.sh +++ b/build.sh @@ -2,9 +2,13 @@ # vi: set tw=0 dotnet pack &&\ -dotnet test test/TestLogger.UnitTests/TestLogger.UnitTests.csproj -p:CollectCoverage=true -p:CoverletOutputFormat=opencover &&\ -dotnet test test/NUnit.Xml.TestLogger.UnitTests/NUnit.Xml.TestLogger.UnitTests.csproj -p:CollectCoverage=true -p:CoverletOutputFormat=opencover &&\ -dotnet test test/JUnit.Xml.TestLogger.UnitTests/JUnit.Xml.TestLogger.UnitTests.csproj -p:CollectCoverage=true -p:CoverletOutputFormat=opencover &&\ +dotnet run --project test/TestLogger.VSTest.UnitTests/TestLogger.VSTest.UnitTests.csproj -- --coverlet --coverlet-output-format cobertura --coverlet-include "[Spekt*]*" &&\ +dotnet run --project test/TestLogger.Mtp.UnitTests/TestLogger.Mtp.UnitTests.csproj -- --coverlet --coverlet-output-format cobertura --coverlet-include "[Spekt*]*" &&\ +dotnet run --project test/NUnit.Xml.TestLogger.UnitTests/NUnit.Xml.TestLogger.UnitTests.csproj -- --coverlet --coverlet-output-format cobertura --coverlet-include "[Spekt*]*" --coverlet-include "[Microsoft.VisualStudio.TestPlatform.Extension*]*" &&\ +dotnet run --project test/JUnit.Xml.TestLogger.UnitTests/JUnit.Xml.TestLogger.UnitTests.csproj -- --coverlet --coverlet-output-format cobertura --coverlet-include "[Spekt*]*" --coverlet-include "[Microsoft.VisualStudio.TestPlatform.Extension*]*" &&\ +dotnet tool restore &&\ +dotnet reportgenerator -reports:"test/TestLogger.VSTest.UnitTests/bin/Debug/net10.0/TestResults/coverage.cobertura.xml;test/TestLogger.Mtp.UnitTests/bin/Debug/net10.0/TestResults/coverage.cobertura.xml;test/NUnit.Xml.TestLogger.UnitTests/bin/Debug/net10.0/TestResults/coverage.cobertura.xml;test/JUnit.Xml.TestLogger.UnitTests/bin/Debug/net10.0/TestResults/coverage.cobertura.xml" -targetdir:coveragereport -reporttypes:"TextSummary;Html" &&\ +cat coveragereport/Summary.txt &&\ dotnet test test/TestLogger.AcceptanceTests/TestLogger.AcceptanceTests.csproj &&\ dotnet test test/TestLogger.PackageTests/TestLogger.PackageTests.csproj &&\ dotnet test test/Xunit.Xml.TestLogger.AcceptanceTests/Xunit.Xml.TestLogger.AcceptanceTests.csproj &&\ diff --git a/src/Directory.Build.props b/src/Directory.Build.props index c11b75d4..86f000b2 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,4 +1,30 @@ + + + + Debug + + + + + bin\$(Configuration)\mtp\ + obj\mtp\ + true + false + $(DefaultItemExcludes);obj\vstest\** + + + + + bin\$(Configuration)\vstest\ + obj\vstest\ + $(DefaultItemExcludes);obj\mtp\** + - \ No newline at end of file + diff --git a/src/JUnit.Xml.Package/JUnit.Xml.Package.csproj b/src/JUnit.Xml.Package/JUnit.Xml.Package.csproj index b8357e29..bfb16965 100644 --- a/src/JUnit.Xml.Package/JUnit.Xml.Package.csproj +++ b/src/JUnit.Xml.Package/JUnit.Xml.Package.csproj @@ -38,9 +38,13 @@ false - - - + + + + + + + @@ -63,10 +67,14 @@ - - - - + + + + + + + + diff --git a/src/JUnit.Xml.Package/JUnitXml.TestLogger.nuspec b/src/JUnit.Xml.Package/JUnitXml.TestLogger.nuspec index f9265f4a..19bcd13a 100644 --- a/src/JUnit.Xml.Package/JUnitXml.TestLogger.nuspec +++ b/src/JUnit.Xml.Package/JUnitXml.TestLogger.nuspec @@ -13,10 +13,12 @@ packageIcon.png - - + + + + - + diff --git a/src/JUnit.Xml.Package/JUnitXml.TestLogger.props b/src/JUnit.Xml.Package/JUnitXml.TestLogger.props index 9c0f3389..925b3eb8 100644 --- a/src/JUnit.Xml.Package/JUnitXml.TestLogger.props +++ b/src/JUnit.Xml.Package/JUnitXml.TestLogger.props @@ -1,20 +1,20 @@ - - Microsoft.VisualStudio.TestPlatform.Extension.JUnit.Xml.TestLogger.dll + + Spekt.TestLogger.VSTest.dll PreserveNewest False - - Spekt.TestLogger.dll + + Microsoft.VisualStudio.TestPlatform.Extension.JUnit.Xml.TestLogger.dll PreserveNewest False - - + + JUnitXml.TestLogger diff --git a/src/JUnit.Xml.TestLogger/Assembly.cs b/src/JUnit.Xml.TestLogger/Assembly.cs index 045acdab..de2310f7 100644 --- a/src/JUnit.Xml.TestLogger/Assembly.cs +++ b/src/JUnit.Xml.TestLogger/Assembly.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; +#if !DEBUG [assembly: ExcludeFromCodeCoverage] namespace System.Diagnostics.CodeAnalysis; @@ -15,4 +16,5 @@ namespace System.Diagnostics.CodeAnalysis; internal sealed class ExcludeFromCodeCoverageAttribute : Attribute { public ExcludeFromCodeCoverageAttribute() { } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/JUnit.Xml.TestLogger/JUnit.Xml.TestLogger.MTP.csproj b/src/JUnit.Xml.TestLogger/JUnit.Xml.TestLogger.MTP.csproj new file mode 100644 index 00000000..fdef8139 --- /dev/null +++ b/src/JUnit.Xml.TestLogger/JUnit.Xml.TestLogger.MTP.csproj @@ -0,0 +1,28 @@ + + + + netstandard2.0 + Microsoft.VisualStudio.TestPlatform.Extension.JUnit.Xml.TestLogger + false + false + true + false + + + + Microsoft.VisualStudio.TestPlatform.Extension.JUnit.Xml.TestLogger + + + + + + + + + + + + + + + diff --git a/src/JUnit.Xml.TestLogger/JUnit.Xml.TestLogger.csproj b/src/JUnit.Xml.TestLogger/JUnit.Xml.TestLogger.VSTest.csproj similarity index 58% rename from src/JUnit.Xml.TestLogger/JUnit.Xml.TestLogger.csproj rename to src/JUnit.Xml.TestLogger/JUnit.Xml.TestLogger.VSTest.csproj index 70c8749b..f30c871f 100644 --- a/src/JUnit.Xml.TestLogger/JUnit.Xml.TestLogger.csproj +++ b/src/JUnit.Xml.TestLogger/JUnit.Xml.TestLogger.VSTest.csproj @@ -4,6 +4,7 @@ netstandard2.0 Microsoft.VisualStudio.TestPlatform.Extension.JUnit.Xml.TestLogger false + false @@ -11,10 +12,17 @@ - + + + + - + + + + + diff --git a/src/JUnit.Xml.TestLogger/JUnitTestReporter.cs b/src/JUnit.Xml.TestLogger/JUnitTestReporter.cs index 48b13803..98ef20e1 100644 --- a/src/JUnit.Xml.TestLogger/JUnitTestReporter.cs +++ b/src/JUnit.Xml.TestLogger/JUnitTestReporter.cs @@ -6,6 +6,7 @@ namespace Spekt.TestReporter.JUnit using System; using Microsoft.VisualStudio.TestPlatform.Extension.Junit.Xml.TestLogger; using Spekt.TestLogger.Core; + using Spekt.TestLogger.Mtp; internal sealed class JUnitTestReporter : TestReporter { diff --git a/src/JUnit.Xml.TestLogger/JUnitTestReporterExtensions.cs b/src/JUnit.Xml.TestLogger/JUnitTestReporterExtensions.cs index c47c0b99..d69f6eb0 100644 --- a/src/JUnit.Xml.TestLogger/JUnitTestReporterExtensions.cs +++ b/src/JUnit.Xml.TestLogger/JUnitTestReporterExtensions.cs @@ -5,6 +5,7 @@ namespace Spekt.TestReporter.JUnit { using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.Extensions; + using Spekt.TestLogger.Mtp; public static class JUnitTestReporterExtensions { diff --git a/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs b/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs index b04c00b5..c71ed98f 100644 --- a/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs +++ b/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs @@ -4,7 +4,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Extension.Junit.Xml.TestLogger { using Microsoft.VisualStudio.TestPlatform.ObjectModel; - using Spekt.TestLogger; + using Spekt.TestLogger.VSTest; [FriendlyName(FriendlyName)] [ExtensionUri(ExtensionUri)] diff --git a/src/JUnit.Xml.TestLogger/JunitXmlSerializer.cs b/src/JUnit.Xml.TestLogger/JunitXmlSerializer.cs index 4389b3a5..44d01b21 100644 --- a/src/JUnit.Xml.TestLogger/JunitXmlSerializer.cs +++ b/src/JUnit.Xml.TestLogger/JunitXmlSerializer.cs @@ -10,8 +10,6 @@ namespace Microsoft.VisualStudio.TestPlatform.Extension.Junit.Xml.TestLogger using System.Linq; using System.Text; using System.Xml.Linq; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Spekt.TestLogger.Core; using Spekt.TestLogger.Utilities; diff --git a/src/NUnit.Xml.Package/NUnit.Xml.Package.csproj b/src/NUnit.Xml.Package/NUnit.Xml.Package.csproj index 04ee3816..e5086d40 100644 --- a/src/NUnit.Xml.Package/NUnit.Xml.Package.csproj +++ b/src/NUnit.Xml.Package/NUnit.Xml.Package.csproj @@ -35,9 +35,13 @@ packageIcon.png - - - + + + + + + + @@ -60,6 +64,15 @@ + + + + + + + + + diff --git a/src/NUnit.Xml.Package/NUnitXml.TestLogger.nuspec b/src/NUnit.Xml.Package/NUnitXml.TestLogger.nuspec index 00467fd2..29712e46 100644 --- a/src/NUnit.Xml.Package/NUnitXml.TestLogger.nuspec +++ b/src/NUnit.Xml.Package/NUnitXml.TestLogger.nuspec @@ -13,12 +13,14 @@ packageIcon.png - - + + + + - + diff --git a/src/NUnit.Xml.Package/NUnitXml.TestLogger.props b/src/NUnit.Xml.Package/NUnitXml.TestLogger.props index c8a0844a..2f6a5cfe 100644 --- a/src/NUnit.Xml.Package/NUnitXml.TestLogger.props +++ b/src/NUnit.Xml.Package/NUnitXml.TestLogger.props @@ -1,20 +1,20 @@ - - Microsoft.VisualStudio.TestPlatform.Extension.NUnit.Xml.TestLogger.dll + + Spekt.TestLogger.VSTest.dll PreserveNewest False - - Spekt.TestLogger.dll + + Microsoft.VisualStudio.TestPlatform.Extension.NUnit.Xml.TestLogger.dll PreserveNewest False - - + + NUnitXml.TestLogger diff --git a/src/NUnit.Xml.TestLogger/Assembly.cs b/src/NUnit.Xml.TestLogger/Assembly.cs index 9f5b1d02..114fad9f 100644 --- a/src/NUnit.Xml.TestLogger/Assembly.cs +++ b/src/NUnit.Xml.TestLogger/Assembly.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; +#if !DEBUG [assembly: ExcludeFromCodeCoverage] namespace System.Diagnostics.CodeAnalysis; @@ -15,4 +16,5 @@ namespace System.Diagnostics.CodeAnalysis; internal sealed class ExcludeFromCodeCoverageAttribute : Attribute { public ExcludeFromCodeCoverageAttribute() { } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/NUnit.Xml.TestLogger/NUnit.Xml.TestLogger.MTP.csproj b/src/NUnit.Xml.TestLogger/NUnit.Xml.TestLogger.MTP.csproj new file mode 100644 index 00000000..912f4c24 --- /dev/null +++ b/src/NUnit.Xml.TestLogger/NUnit.Xml.TestLogger.MTP.csproj @@ -0,0 +1,28 @@ + + + + netstandard2.0 + Microsoft.VisualStudio.TestPlatform.Extension.NUnit.Xml.TestLogger + false + false + true + false + + + + Microsoft.VisualStudio.TestPlatform.Extension.NUnit.Xml.TestLogger + + + + + + + + + + + + + + + diff --git a/src/NUnit.Xml.TestLogger/NUnit.Xml.TestLogger.csproj b/src/NUnit.Xml.TestLogger/NUnit.Xml.TestLogger.VSTest.csproj similarity index 58% rename from src/NUnit.Xml.TestLogger/NUnit.Xml.TestLogger.csproj rename to src/NUnit.Xml.TestLogger/NUnit.Xml.TestLogger.VSTest.csproj index 53f7fe2d..41624715 100644 --- a/src/NUnit.Xml.TestLogger/NUnit.Xml.TestLogger.csproj +++ b/src/NUnit.Xml.TestLogger/NUnit.Xml.TestLogger.VSTest.csproj @@ -4,6 +4,7 @@ netstandard2.0 Microsoft.VisualStudio.TestPlatform.Extension.NUnit.Xml.TestLogger false + false @@ -11,11 +12,18 @@ - + - + + + + + + + + diff --git a/src/NUnit.Xml.TestLogger/NUnitTestReporter.cs b/src/NUnit.Xml.TestLogger/NUnitTestReporter.cs index 36b29f24..6b3a608b 100644 --- a/src/NUnit.Xml.TestLogger/NUnitTestReporter.cs +++ b/src/NUnit.Xml.TestLogger/NUnitTestReporter.cs @@ -6,6 +6,7 @@ namespace Spekt.TestReporter.NUnit using System; using Microsoft.VisualStudio.TestPlatform.Extension.NUnit.Xml.TestLogger; using Spekt.TestLogger.Core; + using Spekt.TestLogger.Mtp; internal sealed class NUnitTestReporter : TestReporter { diff --git a/src/NUnit.Xml.TestLogger/NUnitTestReporterExtensions.cs b/src/NUnit.Xml.TestLogger/NUnitTestReporterExtensions.cs index 9e98467d..c27ec209 100644 --- a/src/NUnit.Xml.TestLogger/NUnitTestReporterExtensions.cs +++ b/src/NUnit.Xml.TestLogger/NUnitTestReporterExtensions.cs @@ -5,6 +5,7 @@ namespace Spekt.TestReporter.NUnit { using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.Extensions; + using Spekt.TestLogger.Mtp; public static class NUnitTestReporterExtensions { diff --git a/src/NUnit.Xml.TestLogger/NUnitXmlSerializer.cs b/src/NUnit.Xml.TestLogger/NUnitXmlSerializer.cs index fe4e4992..6d6cdaf9 100644 --- a/src/NUnit.Xml.TestLogger/NUnitXmlSerializer.cs +++ b/src/NUnit.Xml.TestLogger/NUnitXmlSerializer.cs @@ -10,7 +10,6 @@ namespace Microsoft.VisualStudio.TestPlatform.Extension.NUnit.Xml.TestLogger using System.Linq; using System.Text; using System.Xml.Linq; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Spekt.TestLogger.Core; using Spekt.TestLogger.Utilities; diff --git a/src/NUnit.Xml.TestLogger/NUnitXmlTestLogger.cs b/src/NUnit.Xml.TestLogger/NUnitXmlTestLogger.cs index 6bacf043..2248c804 100644 --- a/src/NUnit.Xml.TestLogger/NUnitXmlTestLogger.cs +++ b/src/NUnit.Xml.TestLogger/NUnitXmlTestLogger.cs @@ -4,7 +4,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Extension.NUnit.Xml.TestLogger { using Microsoft.VisualStudio.TestPlatform.ObjectModel; - using Spekt.TestLogger; + using Spekt.TestLogger.VSTest; [FriendlyName(FriendlyName)] [ExtensionUri(ExtensionUri)] diff --git a/src/TestLogger.MTPv1/TestLogger.MTPv1.csproj b/src/TestLogger.MTPv1/TestLogger.MTPv1.csproj index 4178efef..bb6d2098 100644 --- a/src/TestLogger.MTPv1/TestLogger.MTPv1.csproj +++ b/src/TestLogger.MTPv1/TestLogger.MTPv1.csproj @@ -16,11 +16,10 @@ - + - diff --git a/src/TestLogger/Assembly.cs b/src/TestLogger/Assembly.cs index 7dae9107..17a26c52 100644 --- a/src/TestLogger/Assembly.cs +++ b/src/TestLogger/Assembly.cs @@ -10,7 +10,9 @@ [assembly: InternalsVisibleTo("Json.TestLogger, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e5cffe1b79f1b3f91edadf0ed6fc7fd90592aa75f11277304acf23944d793e8ee57cfc38d0d16008fa2ffed1b09ccdde1c4291ca385c8234a85f08298fe4af491e57643200680d5a3a964d67882c8b03abd7d31c6978b59cb26217134b852ddd114e8b3f4da9ee5ad18a7be202af99529dfb34cd9dcde0a5923c65ca4e6aafa5")] [assembly: InternalsVisibleTo("Json.TestAdapter, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e5cffe1b79f1b3f91edadf0ed6fc7fd90592aa75f11277304acf23944d793e8ee57cfc38d0d16008fa2ffed1b09ccdde1c4291ca385c8234a85f08298fe4af491e57643200680d5a3a964d67882c8b03abd7d31c6978b59cb26217134b852ddd114e8b3f4da9ee5ad18a7be202af99529dfb34cd9dcde0a5923c65ca4e6aafa5")] -[assembly: InternalsVisibleTo("TestLogger.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e5cffe1b79f1b3f91edadf0ed6fc7fd90592aa75f11277304acf23944d793e8ee57cfc38d0d16008fa2ffed1b09ccdde1c4291ca385c8234a85f08298fe4af491e57643200680d5a3a964d67882c8b03abd7d31c6978b59cb26217134b852ddd114e8b3f4da9ee5ad18a7be202af99529dfb34cd9dcde0a5923c65ca4e6aafa5")] +[assembly: InternalsVisibleTo("TestLogger.VSTest.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e5cffe1b79f1b3f91edadf0ed6fc7fd90592aa75f11277304acf23944d793e8ee57cfc38d0d16008fa2ffed1b09ccdde1c4291ca385c8234a85f08298fe4af491e57643200680d5a3a964d67882c8b03abd7d31c6978b59cb26217134b852ddd114e8b3f4da9ee5ad18a7be202af99529dfb34cd9dcde0a5923c65ca4e6aafa5")] +[assembly: InternalsVisibleTo("TestLogger.Mtp.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e5cffe1b79f1b3f91edadf0ed6fc7fd90592aa75f11277304acf23944d793e8ee57cfc38d0d16008fa2ffed1b09ccdde1c4291ca385c8234a85f08298fe4af491e57643200680d5a3a964d67882c8b03abd7d31c6978b59cb26217134b852ddd114e8b3f4da9ee5ad18a7be202af99529dfb34cd9dcde0a5923c65ca4e6aafa5")] +#if !DEBUG [assembly: ExcludeFromCodeCoverage] namespace System.Diagnostics.CodeAnalysis; @@ -19,4 +21,5 @@ namespace System.Diagnostics.CodeAnalysis; internal sealed class ExcludeFromCodeCoverageAttribute : Attribute { public ExcludeFromCodeCoverageAttribute() { } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/TestLogger/Core/DefaultLoggerParameterNames.cs b/src/TestLogger/Core/DefaultLoggerParameterNames.cs new file mode 100644 index 00000000..bd56b369 --- /dev/null +++ b/src/TestLogger/Core/DefaultLoggerParameterNames.cs @@ -0,0 +1,16 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.Core +{ + /// + /// Default logger parameter names. + /// + public static class DefaultLoggerParameterNames + { + /// + /// Test run directory parameter name. + /// + public const string TestRunDirectory = "TestRunDirectory"; + } +} diff --git a/src/TestLogger/Core/ITestRun.cs b/src/TestLogger/Core/ITestRun.cs index d0372d2c..a1b629a1 100644 --- a/src/TestLogger/Core/ITestRun.cs +++ b/src/TestLogger/Core/ITestRun.cs @@ -3,16 +3,13 @@ namespace Spekt.TestLogger.Core { - using Spekt.TestLogger.Extensions; using Spekt.TestLogger.Platform; public interface ITestRun { - LoggerConfiguration LoggerConfiguration { get; } + LoggerConfiguration LoggerConfiguration { get; set; } - TestRunConfiguration RunConfiguration { get; } - - ITestAdapterFactory AdapterFactory { get; } + TestRunConfiguration RunConfiguration { get; set; } ITestResultStore Store { get; } diff --git a/src/TestLogger/Core/ITestRunBuilder.cs b/src/TestLogger/Core/ITestRunBuilder.cs index 53df7105..5174eac1 100644 --- a/src/TestLogger/Core/ITestRunBuilder.cs +++ b/src/TestLogger/Core/ITestRunBuilder.cs @@ -3,7 +3,6 @@ namespace Spekt.TestLogger.Core { - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Spekt.TestLogger.Platform; public interface ITestRunBuilder @@ -14,11 +13,6 @@ public interface ITestRunBuilder ITestRunBuilder WithSerializer(ITestResultSerializer serializer); - // TODO: Should ITestRunBuilder be platform-agnostic? - // TestLoggerEvents is VSTest-specific. - // It may be good to refactor this so that ITestRunBuilder is platform-agnostic, and move this somewhere else. - ITestRunBuilder Subscribe(TestLoggerEvents loggerEvents); - ITestRunBuilder WithFileSystem(IFileSystem fileSystem); ITestRunBuilder WithConsoleOutput(IConsoleOutput consoleOutput); diff --git a/src/TestLogger/Core/LoggerConfiguration.cs b/src/TestLogger/Core/LoggerConfiguration.cs index d24c1729..f8ad68c0 100644 --- a/src/TestLogger/Core/LoggerConfiguration.cs +++ b/src/TestLogger/Core/LoggerConfiguration.cs @@ -6,7 +6,6 @@ namespace Spekt.TestLogger.Core using System; using System.Collections.Generic; using System.IO; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; /// /// Logger configuration provided by the test platform and command line diff --git a/src/TestLogger/Core/TestCaseNameParser.cs b/src/TestLogger/Core/TestCaseNameParser.cs index d9d60cb6..f3652773 100644 --- a/src/TestLogger/Core/TestCaseNameParser.cs +++ b/src/TestLogger/Core/TestCaseNameParser.cs @@ -5,10 +5,12 @@ namespace Spekt.TestLogger.Core { using System; using System.Text.RegularExpressions; - using Microsoft.Testing.Platform.Extensions.Messages; using Spekt.TestLogger.Platform; - public class TestCaseNameParser + /// + /// Parses test case names. + /// + public partial class TestCaseNameParser { public const string TestCaseParserUnknownNamespace = "UnknownNamespace"; public const string TestCaseParserUnknownType = "UnknownType"; @@ -43,104 +45,6 @@ public TestCaseNameParser(IConsoleOutput consoleOutput) this.consoleOutput = consoleOutput ?? throw new ArgumentNullException(nameof(consoleOutput)); } - /// - /// This method parses the TestMethodIdentifierProperty from MTP to extract Namespace, Type and Method name. - /// MTP sometimes provides the full type name in the Namespace property, so this method handles that case. - /// - /// The TestMethodIdentifierProperty from MTP. - /// - /// An instance of ParsedName containing the parsed results. - /// - public ParsedName Parse(TestMethodIdentifierProperty methodIdentifier) - { - if (methodIdentifier == null) - { - return new ParsedName( - TestCaseParserUnknownNamespace, - TestCaseParserUnknownType, - string.Empty); - } - - string @namespace = methodIdentifier.Namespace; - string type = methodIdentifier.TypeName; - string method = methodIdentifier.MethodName; - - // Check if the namespace contains the type name (the problematic case e.g. XUnit) - // This happens when MTP's Namespace property contains the full type name - if (!string.IsNullOrEmpty(@namespace) && !string.IsNullOrEmpty(type)) - { - // NEW CASE: Type contains the full namespace + class name - // If type contains the namespace, extract the actual class name - if (type.StartsWith(@namespace + ".")) - { - type = type.Substring(@namespace.Length + 1); - } - - // If namespace ends with the type name, extract the actual namespace - else if (@namespace.EndsWith("." + type)) - { - @namespace = @namespace.Substring(0, @namespace.Length - type.Length - 1); - } - - // If namespace equals the type name, it's a global namespace - else if (@namespace == type) - { - @namespace = string.Empty; - } - } - - return new ParsedName( - @namespace ?? string.Empty, - type ?? string.Empty, - method ?? string.Empty); - } - - /// - /// This method parses test information from either a TestMethodIdentifierProperty or TestNode. - /// When TestMethodIdentifierProperty is provided, it handles MTP-specific parsing where the Type field - /// may contain the full namespace + class name. When TestMethodIdentifierProperty is null, it falls back - /// to parsing the TestNode's UID for test frameworks like NUnit that don't emit TestMethodIdentifierProperty. - /// - /// The TestMethodIdentifierProperty from MTP (can be null). - /// The TestNode to use for fallback parsing (can be null). - /// - /// A tuple containing the parsed namespace, type, method, and fully qualified name. - /// - public (string Namespace, string Type, string Method, string FullyQualifiedName) Parse(TestMethodIdentifierProperty methodIdentifier, TestNode testNode) - { - if (methodIdentifier != null) - { - var parsedName = this.Parse(methodIdentifier); - var fqn = string.IsNullOrEmpty(parsedName.Namespace) - ? $"{parsedName.Type}.{parsedName.Method}" - : $"{parsedName.Namespace}.{parsedName.Type}.{parsedName.Method}"; - - return (parsedName.Namespace, parsedName.Type, parsedName.Method, fqn); - } - else if (testNode != null) - { - // Fallback to parsing the TestNode's UID (e.g. for NUnit since it doesn't emit TestMethodIdentifierProperty) - var displayName = testNode.Uid; - var parsedName = this.Parse(displayName); - - if (parsedName.Namespace != TestCaseParserUnknownNamespace) - { - // Successfully parsed the display name - return (parsedName.Namespace, parsedName.Type, parsedName.Method, displayName); - } - else - { - // Could not parse the display name, use Unknown values - return ("UnknownNamespace", "UnknownType", "UnknownMethod", "UnknownFullyQualifiedName"); - } - } - else - { - // Neither methodIdentifier nor testNode provided - return ("UnknownNamespace", "UnknownType", "UnknownMethod", "UnknownFullyQualifiedName"); - } - } - /// /// This method attempts to parse out a Namespace, Type and Method name from a given string. /// When a clearly invalid output is encountered, a message is written to the console. diff --git a/src/TestLogger/Core/TestMessageInfo.cs b/src/TestLogger/Core/TestMessageInfo.cs index 344fd981..83233d4f 100644 --- a/src/TestLogger/Core/TestMessageInfo.cs +++ b/src/TestLogger/Core/TestMessageInfo.cs @@ -4,7 +4,6 @@ namespace Spekt.TestLogger.Core { using System; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; /// /// A message generated during the test run. diff --git a/src/TestLogger/Core/TestMessageLevel.cs b/src/TestLogger/Core/TestMessageLevel.cs new file mode 100644 index 00000000..1f91cd6a --- /dev/null +++ b/src/TestLogger/Core/TestMessageLevel.cs @@ -0,0 +1,26 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.Core +{ + /// + /// Represents the level of a test message. + /// + public enum TestMessageLevel + { + /// + /// Informational message. + /// + Informational = 0, + + /// + /// Warning message. + /// + Warning = 1, + + /// + /// Error message. + /// + Error = 2 + } +} diff --git a/src/TestLogger/Core/TestOutcome.cs b/src/TestLogger/Core/TestOutcome.cs new file mode 100644 index 00000000..73b9509f --- /dev/null +++ b/src/TestLogger/Core/TestOutcome.cs @@ -0,0 +1,36 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.Core +{ + /// + /// Represents the outcome of a test. + /// + public enum TestOutcome + { + /// + /// Test outcome is none. + /// + None = 0, + + /// + /// Test passed. + /// + Passed = 1, + + /// + /// Test failed. + /// + Failed = 2, + + /// + /// Test was skipped. + /// + Skipped = 3, + + /// + /// Test was not found. + /// + NotFound = 4 + } +} diff --git a/src/TestLogger/Core/TestResultInfo.cs b/src/TestLogger/Core/TestResultInfo.cs index 0585f1c0..742bf9f3 100644 --- a/src/TestLogger/Core/TestResultInfo.cs +++ b/src/TestLogger/Core/TestResultInfo.cs @@ -5,7 +5,6 @@ namespace Spekt.TestLogger.Core { using System; using System.Collections.Generic; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; public sealed class TestResultInfo { @@ -28,8 +27,7 @@ public TestResultInfo( List messages, List attachments, IReadOnlyCollection traits, - string executorUri, - TestCase testCase) + string executorUri) { this.Namespace = @namespace; this.Type = type; @@ -50,7 +48,6 @@ public TestResultInfo( this.Attachments = attachments; this.Traits = traits; this.ExecutorUri = executorUri; - this.TestCase = testCase; // Use TestCaseDisplayName by default for reporting. this.DisplayName = this.TestCaseDisplayName; @@ -137,14 +134,14 @@ public IReadOnlyCollection> Properties public string FullTypeName => this.Namespace + "." + this.Type; /// - /// Gets value that originated at . Intended for use within + /// Gets value that originated at test result display name. Intended for use within /// this library by framework specific adapters, to ensure that has the /// proper value. /// internal string TestResultDisplayName { get; } /// - /// Gets value that originated at . Intended for use within + /// Gets value that originated at test case display name. Intended for use within /// this library by framework specific adapters, to ensure that has the /// proper value. /// @@ -152,17 +149,6 @@ public IReadOnlyCollection> Properties internal string FullyQualifiedName { get; } - /// - /// Gets the TestCase instance associated with this result. - /// - /// - /// TestProperty store is maintained in TestCase, required for adapter - /// extensions to retrieve values. - /// Intended for internal usage only. Do not expose for consumer - /// loggers. - /// - internal TestCase TestCase { get; } - public override bool Equals(object obj) { return obj is TestResultInfo info && diff --git a/src/TestLogger/Core/TestResultMessage.cs b/src/TestLogger/Core/TestResultMessage.cs new file mode 100644 index 00000000..2d430e92 --- /dev/null +++ b/src/TestLogger/Core/TestResultMessage.cs @@ -0,0 +1,42 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.Core +{ + /// + /// Represents a test result message. + /// + public sealed class TestResultMessage + { + /// + /// Standard output category. + /// + public const string StandardOutCategory = "StdOutMsgs"; + + /// + /// Standard error category. + /// + public const string StandardErrorCategory = "StdErrMsgs"; + + /// + /// Initializes a new instance of the class. + /// + /// The message category. + /// The message text. + public TestResultMessage(string category, string text) + { + this.Category = category; + this.Text = text; + } + + /// + /// Gets the category. + /// + public string Category { get; } + + /// + /// Gets the text. + /// + public string Text { get; } + } +} diff --git a/src/TestLogger/Core/TestRun.cs b/src/TestLogger/Core/TestRun.cs index 05e990c0..d88fb1c0 100644 --- a/src/TestLogger/Core/TestRun.cs +++ b/src/TestLogger/Core/TestRun.cs @@ -3,23 +3,20 @@ namespace Spekt.TestLogger.Core { - using Spekt.TestLogger.Extensions; using Spekt.TestLogger.Platform; public class TestRun : ITestRun { - public LoggerConfiguration LoggerConfiguration { get; internal set; } + public LoggerConfiguration LoggerConfiguration { get; set; } - public TestRunConfiguration RunConfiguration { get; internal set; } + public TestRunConfiguration RunConfiguration { get; set; } - public ITestAdapterFactory AdapterFactory { get; internal set; } + public ITestResultStore Store { get; set; } - public ITestResultStore Store { get; internal set; } + public ITestResultSerializer Serializer { get; set; } - public ITestResultSerializer Serializer { get; internal set; } + public IConsoleOutput ConsoleOutput { get; set; } - public IConsoleOutput ConsoleOutput { get; internal set; } - - public IFileSystem FileSystem { get; internal set; } + public IFileSystem FileSystem { get; set; } } } \ No newline at end of file diff --git a/src/TestLogger/Core/TestRunBuilder.cs b/src/TestLogger/Core/TestRunBuilder.cs index 57dce13d..eaf5e5c6 100644 --- a/src/TestLogger/Core/TestRunBuilder.cs +++ b/src/TestLogger/Core/TestRunBuilder.cs @@ -4,8 +4,6 @@ namespace Spekt.TestLogger.Core { using System; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; - using Spekt.TestLogger.Extensions; using Spekt.TestLogger.Platform; public class TestRunBuilder : ITestRunBuilder @@ -17,7 +15,6 @@ public TestRunBuilder() this.testRun = new TestRun { RunConfiguration = new TestRunConfiguration(), - AdapterFactory = new TestAdapterFactory(), }; } @@ -39,24 +36,6 @@ public ITestRunBuilder WithSerializer(ITestResultSerializer serializer) return this; } - public ITestRunBuilder Subscribe(TestLoggerEvents loggerEvents) - { - if (loggerEvents == null) - { - throw new ArgumentNullException(nameof(loggerEvents)); - } - - loggerEvents.TestRunStart += (_, eventArgs) => - { - this.testRun.RunConfiguration = this.testRun.Start(eventArgs); - }; - loggerEvents.TestRunMessage += (_, eventArgs) => this.TraceAndThrow(() => this.testRun.Message(eventArgs), "TestRunMessage"); - loggerEvents.TestResult += (_, eventArgs) => this.TraceAndThrow(() => this.testRun.Result(eventArgs.Result), "TestResult"); - loggerEvents.TestRunComplete += (_, eventArgs) => this.TraceAndThrow(() => this.testRun.Complete(eventArgs), "TestRunComplete"); - - return this; - } - public ITestRunBuilder WithFileSystem(IFileSystem fileSystem) { this.testRun.FileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); diff --git a/src/TestLogger/Core/TestRunCompleteWorkflow.cs b/src/TestLogger/Core/TestRunCompleteWorkflow.cs index 33d1c31b..edae8019 100644 --- a/src/TestLogger/Core/TestRunCompleteWorkflow.cs +++ b/src/TestLogger/Core/TestRunCompleteWorkflow.cs @@ -7,41 +7,20 @@ namespace Spekt.TestLogger.Core using System.Collections.Generic; using System.Globalization; using System.IO; - using System.Linq; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Spekt.TestLogger.Platform; - using Spekt.TestLogger.Utilities; public static class TestRunCompleteWorkflow { - public static void Complete(this ITestRun testRun, TestRunCompleteEventArgs completeEvent) - { - var logFilePath = testRun.LoggerConfiguration - .GetFormattedLogFilePath(testRun.RunConfiguration); - var resultsDirectory = Path.GetDirectoryName(logFilePath); - var attachments = completeEvent.AttachmentSets.SelectMany(x => x.ToAttachments(baseDirectory: resultsDirectory, makeRelativePaths: testRun.LoggerConfiguration.UseRelativeAttachmentPaths)).ToList(); - - Complete(testRun, attachments); - } - - public static void Complete(this ITestRun testRun, IReadOnlyCollection testAttachmentInfos) + public static void Complete( + this ITestRun testRun, + IReadOnlyCollection testAttachmentInfos, + List results, + List messages) { // Update the test run complete timestamp and run level attachments testRun.RunConfiguration.EndTime = DateTime.UtcNow; testRun.RunConfiguration.Attachments = testAttachmentInfos; - // Freeze and reset the test result store - testRun.Store.Pop(out var results, out var messages); - - // Transform the results with adapter specific hooks - var transformedResults = results; - if (transformedResults.Any()) - { - var executorUri = transformedResults[0].ExecutorUri; - var adapter = testRun.AdapterFactory.CreateTestAdapter(executorUri); - transformedResults = adapter.TransformResults(results, messages); - } - // Prepare test results file from logger configuration var logFilePath = testRun.LoggerConfiguration .GetFormattedLogFilePath(testRun.RunConfiguration); @@ -50,7 +29,7 @@ public static void Complete(this ITestRun testRun, IReadOnlyCollection - /// (MTP) Processes a and logs any standard output or error messages to the test run. - /// - /// The test run to log messages to. - /// The test node update message containing properties to log. - public static void Message(this ITestRun testRun, TestNodeUpdateMessage testNodeUpdateMessage) - { - foreach (var property in testNodeUpdateMessage.TestNode.Properties) - { -#pragma warning disable TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. - if (property is StandardErrorProperty stdErr) - { - testRun.Message(TestMessageLevel.Error, stdErr.StandardError); - } - else if (property is StandardOutputProperty stdOut) - { - testRun.Message(TestMessageLevel.Informational, stdOut.StandardOutput); - } -#pragma warning restore TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. - } - } - - public static void Message(this ITestRun testRun, TestRunMessageEventArgs messageEvent) - => Message(testRun, messageEvent.Level, messageEvent.Message); - public static void Message(this ITestRun testRun, TestMessageLevel messageLevel, string message) { testRun.Store.Add( diff --git a/src/TestLogger/Core/TestRunResultWorkflow.cs b/src/TestLogger/Core/TestRunResultWorkflow.cs index a25a6aca..b1dc8a54 100644 --- a/src/TestLogger/Core/TestRunResultWorkflow.cs +++ b/src/TestLogger/Core/TestRunResultWorkflow.cs @@ -3,211 +3,15 @@ namespace Spekt.TestLogger.Core { - using System; - using System.Collections.Generic; using System.IO; - using System.Linq; - using System.Reflection; - using Microsoft.Testing.Platform.Extensions.Messages; - using Microsoft.Testing.Platform.Extensions.TestFramework; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; - using Spekt.TestLogger.Utilities; public static class TestRunResultWorkflow { - // Parser instances are created per run so we can throttle error notifications to once per run. - private static TestCaseNameParser parser; - private static LegacyTestCaseNameParser legacyParser; - - // This is only reachable for MTP. - public static void Result(this ITestRun testRun, TestNodeUpdateMessage testNodeUpdateMessage, ITestFramework testFramework) - { - if (testNodeUpdateMessage.TestNode.Properties.SingleOrDefault() is not { } state || - state is InProgressTestNodeStateProperty) - { - return; - } - - // Initialize parsers with console output if not already done - parser ??= new TestCaseNameParser(testRun.ConsoleOutput); - legacyParser ??= new LegacyTestCaseNameParser(testRun.ConsoleOutput); - - testRun.LoggerConfiguration.Values.TryGetValue(LoggerConfiguration.ParserKey, out string parserVal); - - Func sanitize = testRun.Serializer.InputSanitizer.Sanitize; - - var attachments = testNodeUpdateMessage.TestNode.Properties.OfType().ToAttachments(baseDirectory: GetTestResultDirectory(testRun), makeRelativePaths: testRun.LoggerConfiguration.UseRelativeAttachmentPaths).ToList(); - - var (errorMessage, errorStackTrace) = state switch - { - FailedTestNodeStateProperty failed => (failed.Exception.Message ?? failed.Explanation, failed.Exception.StackTrace), - ErrorTestNodeStateProperty error => (error.Exception.Message ?? error.Explanation, error.Exception.StackTrace), - _ => (string.Empty, string.Empty), - }; - - string filePath = null; - int lineNumber = -1; - var traits = new List(); - var messages = new List(); - DateTime startTime = default; - DateTime endTime = default; - TimeSpan duration = default; - - string @namespace = string.Empty; - string type = string.Empty; - string method = string.Empty; - string fqn = string.Empty; - - // Find TestMethodIdentifierProperty if it exists - TestMethodIdentifierProperty methodIdentifier = null; - foreach (var property in testNodeUpdateMessage.TestNode.Properties) - { - if (property is TestFileLocationProperty testFileLocation) - { - filePath = testFileLocation.FilePath; - lineNumber = testFileLocation.LineSpan.Start.Line; - } -#pragma warning disable TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. - else if (property is StandardErrorProperty stdErr) - { - messages.Add(new TestResultMessage(TestResultMessage.StandardErrorCategory, stdErr.StandardError)); - } - else if (property is StandardOutputProperty stdOut) - { - messages.Add(new TestResultMessage(TestResultMessage.StandardOutCategory, stdOut.StandardOutput)); - } -#pragma warning restore TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. - else if (property is TestMetadataProperty metadata) - { - traits.Add(new Trait(metadata.Key, metadata.Value)); - } - else if (property is TestMethodIdentifierProperty methodIdProp) - { - methodIdentifier = methodIdProp; - } - else if (property is TimingProperty timing) - { - startTime = timing.GlobalTiming.StartTime.UtcDateTime; - endTime = timing.GlobalTiming.EndTime.UtcDateTime; - duration = timing.GlobalTiming.Duration; - } - } - - // Parse test information using the unified method - var parseResult = parser.Parse(methodIdentifier, testNodeUpdateMessage.TestNode); - @namespace = parseResult.Namespace; - type = parseResult.Type; - method = parseResult.Method; - fqn = parseResult.FullyQualifiedName; - - var assemblyPath = Assembly.GetEntryAssembly()?.Location; - if (string.IsNullOrEmpty(assemblyPath)) - { - assemblyPath = "UnknownAssembly"; - } - - testRun.Store.Add(new TestResultInfo( - sanitize(@namespace), - sanitize(type), - sanitize(method), - sanitize(fqn), - GetOutcome(state), - sanitize(testNodeUpdateMessage.TestNode.DisplayName), - sanitize(testNodeUpdateMessage.TestNode.DisplayName), - sanitize(assemblyPath), - sanitize(filePath), - lineNumber: lineNumber, - startTime, - endTime, - duration, - sanitize(errorMessage), - sanitize(errorStackTrace), - messages, - attachments, - traits, - testFramework.DisplayName, - null)); // TODO: This ends up being used in ITestAdapter implementations. The usage should be revised to better understand how to fix it. - - static TestOutcome GetOutcome(TestNodeStateProperty state) - { - return state switch - { - PassedTestNodeStateProperty => TestOutcome.Passed, - FailedTestNodeStateProperty or ErrorTestNodeStateProperty or TimeoutTestNodeStateProperty or CancelledTestNodeStateProperty => TestOutcome.Failed, - SkippedTestNodeStateProperty => TestOutcome.Skipped, - _ => TestOutcome.None, // TODO: Should this throw? - }; - } - } - - // This is reachable only for VSTest. - public static void Result(this ITestRun testRun, TestResult result) - { - // Initialize parsers with console output if not already done - parser ??= new TestCaseNameParser(testRun.ConsoleOutput); - legacyParser ??= new LegacyTestCaseNameParser(testRun.ConsoleOutput); - - var fqn = result.TestCase.FullyQualifiedName; - testRun.LoggerConfiguration.Values.TryGetValue(LoggerConfiguration.ParserKey, out string parserVal); - var parsedName = parserVal switch - { - string x when x.Equals("Legacy", StringComparison.OrdinalIgnoreCase) => legacyParser.Parse(fqn), - _ => parser.Parse(fqn), - }; - - // Prepare attachments with relative paths if configured - var attachments = result.Attachments.SelectMany(x => x.ToAttachments(baseDirectory: GetTestResultDirectory(testRun), makeRelativePaths: testRun.LoggerConfiguration.UseRelativeAttachmentPaths)).ToList(); - - Func sanitize = testRun.Serializer.InputSanitizer.Sanitize; - - var traits = GetTraits(result.TestCase, sanitize); - testRun.Store.Add(new TestResultInfo( - sanitize(parsedName.Namespace), - sanitize(parsedName.Type), - sanitize(parsedName.Method), - sanitize(fqn), - result.Outcome, - sanitize(result.DisplayName), - sanitize(result.TestCase.DisplayName), - sanitize(result.TestCase.Source), - sanitize(result.TestCase.CodeFilePath), - result.TestCase.LineNumber, - result.StartTime.UtcDateTime, - result.EndTime.UtcDateTime, - result.Duration, - sanitize(result.ErrorMessage), - sanitize(result.ErrorStackTrace), - result.Messages.Select(x => new TestResultMessage(sanitize(x.Category), sanitize(x.Text))).ToList(), - attachments, - traits, - result.TestCase.ExecutorUri?.ToString(), - result.TestCase)); - } - - private static string GetTestResultDirectory(ITestRun testRun) + public static string GetTestResultDirectory(ITestRun testRun) { var logFilePath = testRun.LoggerConfiguration .GetFormattedLogFilePath(testRun.RunConfiguration); return Path.GetDirectoryName(logFilePath); } - - private static List GetTraits(TestCase testCase, Func sanitize) - { - // Workaround for test platform regression in .NET 10 SDK - // var traits = result.TestCase.Traits.Select(x => new Trait(sanitize(x.Name), sanitize(x.Value))).ToList(); - var traits = new List(); - var traitProperty = testCase.Properties.FirstOrDefault(p => p.Id == "TestObject.Traits"); - if (traitProperty != null) - { - var traitValues = testCase.GetPropertyValue(traitProperty, Enumerable.Empty>()); - - foreach (var kvp in traitValues) - { - traits.Add(new Trait(sanitize(kvp.Key), sanitize(kvp.Value))); - } - } - - return traits; - } } } \ No newline at end of file diff --git a/src/TestLogger/Core/TestRunStartWorkflow.cs b/src/TestLogger/Core/TestRunStartWorkflow.cs index ce89f0da..a12101e0 100644 --- a/src/TestLogger/Core/TestRunStartWorkflow.cs +++ b/src/TestLogger/Core/TestRunStartWorkflow.cs @@ -4,28 +4,9 @@ namespace Spekt.TestLogger.Core { using System; - using System.Linq; - using System.Xml; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; public static class TestRunStartWorkflow { - public static TestRunConfiguration Start(this ITestRun testRun, TestRunStartEventArgs startedEvent) - { - // Extract assembly path and adapter from test run criteria - // TODO validate if the testcase filter or running specific tests is going to break this! - var assemblyPath = startedEvent.TestRunCriteria.Sources.First(); - - // Extract target framework from run settings - var runSettings = new XmlDocument(); - runSettings.LoadXml(startedEvent.TestRunCriteria.TestRunSettings); - var framework = runSettings - .GetElementsByTagName("TargetFrameworkVersion")[0] - .InnerText; - - return Start(testRun, assemblyPath, framework); - } - public static TestRunConfiguration Start(this ITestRun testRun, string assemblyPath, string targetFramework) { return new TestRunConfiguration diff --git a/src/TestLogger/Core/Trait.cs b/src/TestLogger/Core/Trait.cs new file mode 100644 index 00000000..ded0fded --- /dev/null +++ b/src/TestLogger/Core/Trait.cs @@ -0,0 +1,32 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.Core +{ + /// + /// Represents a trait (category) for a test. + /// + public sealed class Trait + { + /// + /// Initializes a new instance of the class. + /// + /// The trait name. + /// The trait value. + public Trait(string name, string value) + { + this.Name = name; + this.Value = value; + } + + /// + /// Gets the trait name. + /// + public string Name { get; } + + /// + /// Gets the trait value. + /// + public string Value { get; } + } +} diff --git a/src/TestLogger/Mtp/Core/MtpTestCaseNameParserExtensions.cs b/src/TestLogger/Mtp/Core/MtpTestCaseNameParserExtensions.cs new file mode 100644 index 00000000..355c64ba --- /dev/null +++ b/src/TestLogger/Mtp/Core/MtpTestCaseNameParserExtensions.cs @@ -0,0 +1,78 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.Mtp.Core +{ + using Microsoft.Testing.Platform.Extensions.Messages; + using Spekt.TestLogger.Core; + + public static class MtpTestCaseNameParserExtensions + { + public static ParsedName Parse(this TestCaseNameParser parser, TestMethodIdentifierProperty methodIdentifier) + { + if (methodIdentifier == null) + { + return new ParsedName( + TestCaseNameParser.TestCaseParserUnknownNamespace, + TestCaseNameParser.TestCaseParserUnknownType, + string.Empty); + } + + string @namespace = methodIdentifier.Namespace; + string type = methodIdentifier.TypeName; + string method = methodIdentifier.MethodName; + + if (!string.IsNullOrEmpty(@namespace) && !string.IsNullOrEmpty(type)) + { + if (type.StartsWith(@namespace + ".")) + { + type = type.Substring(@namespace.Length + 1); + } + else if (@namespace.EndsWith("." + type)) + { + @namespace = @namespace.Substring(0, @namespace.Length - type.Length - 1); + } + else if (@namespace == type) + { + @namespace = string.Empty; + } + } + + return new ParsedName( + @namespace ?? string.Empty, + type ?? string.Empty, + method ?? string.Empty); + } + + public static (string Namespace, string Type, string Method, string FullyQualifiedName) Parse(this TestCaseNameParser parser, TestMethodIdentifierProperty methodIdentifier, TestNode testNode) + { + if (methodIdentifier != null) + { + var parsedName = parser.Parse(methodIdentifier); + var fqn = string.IsNullOrEmpty(parsedName.Namespace) + ? $"{parsedName.Type}.{parsedName.Method}" + : $"{parsedName.Namespace}.{parsedName.Type}.{parsedName.Method}"; + + return (parsedName.Namespace, parsedName.Type, parsedName.Method, fqn); + } + else if (testNode != null) + { + var displayName = testNode.Uid; + var parsedName = parser.Parse(displayName); + + if (parsedName.Namespace != TestCaseNameParser.TestCaseParserUnknownNamespace) + { + return (parsedName.Namespace, parsedName.Type, parsedName.Method, displayName); + } + else + { + return ("UnknownNamespace", "UnknownType", "UnknownMethod", "UnknownFullyQualifiedName"); + } + } + else + { + return ("UnknownNamespace", "UnknownType", "UnknownMethod", "UnknownFullyQualifiedName"); + } + } + } +} diff --git a/src/TestLogger/Mtp/Core/MtpTestRunMessageWorkflow.cs b/src/TestLogger/Mtp/Core/MtpTestRunMessageWorkflow.cs new file mode 100644 index 00000000..6eb99e9f --- /dev/null +++ b/src/TestLogger/Mtp/Core/MtpTestRunMessageWorkflow.cs @@ -0,0 +1,28 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.Mtp.Core +{ + using Microsoft.Testing.Platform.Extensions.Messages; + using Spekt.TestLogger.Core; + + public static class MtpTestRunMessageWorkflow + { + public static void Message(this ITestRun testRun, TestNodeUpdateMessage testNodeUpdateMessage) + { + foreach (var property in testNodeUpdateMessage.TestNode.Properties) + { +#pragma warning disable TPEXP + if (property is StandardErrorProperty stdErr) + { + testRun.Message(TestMessageLevel.Error, stdErr.StandardError); + } + else if (property is StandardOutputProperty stdOut) + { + testRun.Message(TestMessageLevel.Informational, stdOut.StandardOutput); + } +#pragma warning restore TPEXP + } + } + } +} diff --git a/src/TestLogger/Mtp/Core/MtpTestRunResultWorkflow.cs b/src/TestLogger/Mtp/Core/MtpTestRunResultWorkflow.cs new file mode 100644 index 00000000..18f86113 --- /dev/null +++ b/src/TestLogger/Mtp/Core/MtpTestRunResultWorkflow.cs @@ -0,0 +1,137 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.Mtp.Core +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Reflection; + using Microsoft.Testing.Platform.Extensions.Messages; + using Microsoft.Testing.Platform.Extensions.TestFramework; + using Spekt.TestLogger.Core; + using Spekt.TestLogger.Mtp.Utilities; + + public static class MtpTestRunResultWorkflow + { + private static TestCaseNameParser parser; + private static LegacyTestCaseNameParser legacyParser; + + public static void Result(this ITestRun testRun, TestNodeUpdateMessage testNodeUpdateMessage, ITestFramework testFramework) + { + if (testNodeUpdateMessage.TestNode.Properties.SingleOrDefault() is not { } state || + state is InProgressTestNodeStateProperty) + { + return; + } + + parser ??= new TestCaseNameParser(testRun.ConsoleOutput); + legacyParser ??= new LegacyTestCaseNameParser(testRun.ConsoleOutput); + + testRun.LoggerConfiguration.Values.TryGetValue(LoggerConfiguration.ParserKey, out string parserVal); + + Func sanitize = testRun.Serializer.InputSanitizer.Sanitize; + + var attachments = testNodeUpdateMessage.TestNode.Properties.OfType().ToAttachments(baseDirectory: TestRunResultWorkflow.GetTestResultDirectory(testRun), makeRelativePaths: testRun.LoggerConfiguration.UseRelativeAttachmentPaths).ToList(); + + var (errorMessage, errorStackTrace) = state switch + { + FailedTestNodeStateProperty failed => (failed.Exception.Message ?? failed.Explanation, failed.Exception.StackTrace), + ErrorTestNodeStateProperty error => (error.Exception.Message ?? error.Explanation, error.Exception.StackTrace), + _ => (string.Empty, string.Empty), + }; + + string filePath = null; + int lineNumber = -1; + var traits = new List(); + var messages = new List(); + DateTime startTime = default; + DateTime endTime = default; + TimeSpan duration = default; + + string @namespace = string.Empty; + string type = string.Empty; + string method = string.Empty; + string fqn = string.Empty; + + TestMethodIdentifierProperty methodIdentifier = null; + foreach (var property in testNodeUpdateMessage.TestNode.Properties) + { + if (property is TestFileLocationProperty testFileLocation) + { + filePath = testFileLocation.FilePath; + lineNumber = testFileLocation.LineSpan.Start.Line; + } +#pragma warning disable TPEXP + else if (property is StandardErrorProperty stdErr) + { + messages.Add(new TestResultMessage(TestResultMessage.StandardErrorCategory, stdErr.StandardError)); + } + else if (property is StandardOutputProperty stdOut) + { + messages.Add(new TestResultMessage(TestResultMessage.StandardOutCategory, stdOut.StandardOutput)); + } +#pragma warning restore TPEXP + else if (property is TestMetadataProperty metadata) + { + traits.Add(new Trait(metadata.Key, metadata.Value)); + } + else if (property is TestMethodIdentifierProperty methodIdProp) + { + methodIdentifier = methodIdProp; + } + else if (property is TimingProperty timing) + { + startTime = timing.GlobalTiming.StartTime.UtcDateTime; + endTime = timing.GlobalTiming.EndTime.UtcDateTime; + duration = timing.GlobalTiming.Duration; + } + } + + var parseResult = parser.Parse(methodIdentifier, testNodeUpdateMessage.TestNode); + @namespace = parseResult.Namespace; + type = parseResult.Type; + method = parseResult.Method; + fqn = parseResult.FullyQualifiedName; + + var assemblyPath = Assembly.GetEntryAssembly()?.Location; + if (string.IsNullOrEmpty(assemblyPath)) + { + assemblyPath = "UnknownAssembly"; + } + + testRun.Store.Add(new TestResultInfo( + sanitize(@namespace), + sanitize(type), + sanitize(method), + sanitize(fqn), + GetOutcome(state), + sanitize(testNodeUpdateMessage.TestNode.DisplayName), + sanitize(testNodeUpdateMessage.TestNode.DisplayName), + sanitize(assemblyPath), + sanitize(filePath), + lineNumber: lineNumber, + startTime, + endTime, + duration, + sanitize(errorMessage), + sanitize(errorStackTrace), + messages, + attachments, + traits, + testFramework.DisplayName)); + + static TestOutcome GetOutcome(TestNodeStateProperty state) + { + return state switch + { + PassedTestNodeStateProperty => TestOutcome.Passed, + FailedTestNodeStateProperty or ErrorTestNodeStateProperty or TimeoutTestNodeStateProperty or CancelledTestNodeStateProperty => TestOutcome.Failed, + SkippedTestNodeStateProperty => TestOutcome.Skipped, + _ => TestOutcome.None, + }; + } + } + } +} diff --git a/src/TestLogger/TestReporter.cs b/src/TestLogger/Mtp/TestReporter.cs similarity index 96% rename from src/TestLogger/TestReporter.cs rename to src/TestLogger/Mtp/TestReporter.cs index 5a97531d..f3b7f3bb 100644 --- a/src/TestLogger/TestReporter.cs +++ b/src/TestLogger/Mtp/TestReporter.cs @@ -1,7 +1,7 @@ // Copyright (c) Spekt Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Spekt.TestReporter +namespace Spekt.TestLogger.Mtp { using System; using System.Collections.Generic; @@ -16,10 +16,10 @@ namespace Spekt.TestReporter using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Services; using Microsoft.Testing.Platform.TestHost; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Spekt.TestLogger.Core; + using Spekt.TestLogger.Mtp.Core; + using Spekt.TestLogger.Mtp.Utilities; using Spekt.TestLogger.Platform; - using Spekt.TestLogger.Utilities; /// /// Base test reporter implementation for Microsoft.Testing.Platform. @@ -104,7 +104,8 @@ public Task OnTestSessionFinishingAsync(SessionUid sessionUid, CancellationToken public Task OnTestSessionFinishingAsync(ITestSessionContext testSessionContext) #endif { - this.testRun.Complete(this.testAttachmentInfos); + this.testRun.Store.Pop(out var results, out var messages); + this.testRun.Complete(this.testAttachmentInfos, results, messages); return Task.CompletedTask; } diff --git a/src/TestLogger/TestReporterCommandLineProvider.cs b/src/TestLogger/Mtp/TestReporterCommandLineProvider.cs similarity index 99% rename from src/TestLogger/TestReporterCommandLineProvider.cs rename to src/TestLogger/Mtp/TestReporterCommandLineProvider.cs index a9a33faf..a689be75 100644 --- a/src/TestLogger/TestReporterCommandLineProvider.cs +++ b/src/TestLogger/Mtp/TestReporterCommandLineProvider.cs @@ -1,7 +1,7 @@ // Copyright (c) Spekt Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Spekt.TestReporter +namespace Spekt.TestLogger.Mtp { using System.Collections.Generic; using System.Linq; diff --git a/src/TestLogger/Utilities/ArtifactExtensions.cs b/src/TestLogger/Mtp/Utilities/ArtifactExtensions.cs similarity index 98% rename from src/TestLogger/Utilities/ArtifactExtensions.cs rename to src/TestLogger/Mtp/Utilities/ArtifactExtensions.cs index d39cb07b..cbab22c7 100644 --- a/src/TestLogger/Utilities/ArtifactExtensions.cs +++ b/src/TestLogger/Mtp/Utilities/ArtifactExtensions.cs @@ -1,7 +1,7 @@ // Copyright (c) Spekt Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Spekt.TestLogger.Utilities +namespace Spekt.TestLogger.Mtp.Utilities { using System; using System.Collections.Generic; diff --git a/src/TestLogger/TestLogger.MTP.csproj b/src/TestLogger/TestLogger.MTP.csproj new file mode 100644 index 00000000..b6ac2b8e --- /dev/null +++ b/src/TestLogger/TestLogger.MTP.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + Spekt.TestLogger.Mtp + $(PackageVersion) + true + embedded + true + $(DefaultItemExcludes);VSTest/** + + + + Spekt.TestLogger + + + + + + + diff --git a/src/TestLogger/TestLogger.csproj b/src/TestLogger/TestLogger.VSTest.csproj similarity index 70% rename from src/TestLogger/TestLogger.csproj rename to src/TestLogger/TestLogger.VSTest.csproj index 77a1dc4f..01ab66b0 100644 --- a/src/TestLogger/TestLogger.csproj +++ b/src/TestLogger/TestLogger.VSTest.csproj @@ -1,12 +1,13 @@ - + netstandard2.0 - Spekt.TestLogger + Spekt.TestLogger.VSTest $(PackageVersion) true embedded true + $(DefaultItemExcludes);Mtp/** @@ -14,8 +15,7 @@ - - + diff --git a/src/TestLogger/Utilities/AttachmentSetExtensions.cs b/src/TestLogger/Utilities/AttachmentSetExtensions.cs deleted file mode 100644 index 34c583dc..00000000 --- a/src/TestLogger/Utilities/AttachmentSetExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Spekt Contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -namespace Spekt.TestLogger.Utilities -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; - using Spekt.TestLogger.Core; - - public static class AttachmentSetExtensions - { - public static IEnumerable ToAttachments(this AttachmentSet attachmentSet, string baseDirectory, bool makeRelativePaths) - { - if (makeRelativePaths && !string.IsNullOrEmpty(baseDirectory)) - { - return attachmentSet.Attachments.Select(a => - { - var attachmentPath = GetPathFromUri(a.Uri); - var relativePath = ArtifactExtensions.MakeRelativePath(baseDirectory, attachmentPath); - return new TestAttachmentInfo(relativePath, a.Description); - }); - } - - return attachmentSet.Attachments.Select(a => new - TestAttachmentInfo(GetPathFromUri(a.Uri), a.Description)); - } - - private static string GetPathFromUri(Uri uri) - { - try - { - return uri.LocalPath; - } - catch (InvalidOperationException) - { - return uri.OriginalString; - } - } - } -} \ No newline at end of file diff --git a/src/TestLogger/VSTest/Core/VSTestTestRunBuilderExtensions.cs b/src/TestLogger/VSTest/Core/VSTestTestRunBuilderExtensions.cs new file mode 100644 index 00000000..d220c96c --- /dev/null +++ b/src/TestLogger/VSTest/Core/VSTestTestRunBuilderExtensions.cs @@ -0,0 +1,46 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.VSTest.Core +{ + using System; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Spekt.TestLogger.Core; + + public static class VSTestTestRunBuilderExtensions + { + public static void Subscribe(this ITestRun testRun, TestLoggerEvents loggerEvents) + { + if (testRun == null) + { + throw new ArgumentNullException(nameof(testRun)); + } + + if (loggerEvents == null) + { + throw new ArgumentNullException(nameof(loggerEvents)); + } + + loggerEvents.TestRunStart += (_, eventArgs) => + { + testRun.RunConfiguration = testRun.Start(eventArgs); + }; + loggerEvents.TestRunMessage += (_, eventArgs) => TraceAndThrow(testRun, () => testRun.Message(eventArgs), "TestRunMessage"); + loggerEvents.TestResult += (_, eventArgs) => TraceAndThrow(testRun, () => testRun.Result(eventArgs.Result), "TestResult"); + loggerEvents.TestRunComplete += (_, eventArgs) => TraceAndThrow(testRun, () => testRun.Complete(eventArgs), "TestRunComplete"); + } + + private static void TraceAndThrow(ITestRun testRun, Action action, string source) + { + try + { + action(); + } + catch (Exception) + { + testRun.ConsoleOutput?.WriteError($"Test Logger: Unexpected error in {source} workflow. Please rerun with `dotnet test --diag:log.txt` to see the stacktrace and report the issue at https://github.com/spekt/testlogger/issues/new."); + throw; + } + } + } +} diff --git a/src/TestLogger/VSTest/Core/VSTestTestRunCompleteWorkflow.cs b/src/TestLogger/VSTest/Core/VSTestTestRunCompleteWorkflow.cs new file mode 100644 index 00000000..6a0d8964 --- /dev/null +++ b/src/TestLogger/VSTest/Core/VSTestTestRunCompleteWorkflow.cs @@ -0,0 +1,28 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.VSTest.Core +{ + using System.IO; + using System.Linq; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Spekt.TestLogger.Core; + using Spekt.TestLogger.VSTest.Utilities; + + public static class VSTestTestRunCompleteWorkflow + { + public static void Complete(this ITestRun testRun, TestRunCompleteEventArgs completeEvent) + { + var logFilePath = testRun.LoggerConfiguration + .GetFormattedLogFilePath(testRun.RunConfiguration); + var resultsDirectory = Path.GetDirectoryName(logFilePath); + var attachments = completeEvent.AttachmentSets.SelectMany(x => x.ToAttachments(baseDirectory: resultsDirectory, makeRelativePaths: testRun.LoggerConfiguration.UseRelativeAttachmentPaths)).ToList(); + + testRun.Store.Pop(out var results, out var messages); + + // Results were already transformed per-result in VSTestTestRunResultWorkflow.Result + // using the real VSTest TestCase. No batch transformation needed here. + TestRunCompleteWorkflow.Complete(testRun, attachments, results, messages); + } + } +} diff --git a/src/TestLogger/VSTest/Core/VSTestTestRunMessageWorkflow.cs b/src/TestLogger/VSTest/Core/VSTestTestRunMessageWorkflow.cs new file mode 100644 index 00000000..00453452 --- /dev/null +++ b/src/TestLogger/VSTest/Core/VSTestTestRunMessageWorkflow.cs @@ -0,0 +1,16 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.VSTest.Core +{ + using Spekt.TestLogger.Core; + + public static class VSTestTestRunMessageWorkflow + { + public static void Message(this ITestRun testRun, Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging.TestRunMessageEventArgs messageEvent) + { + var level = (TestMessageLevel)(int)messageEvent.Level; + testRun.Message(level, messageEvent.Message); + } + } +} diff --git a/src/TestLogger/VSTest/Core/VSTestTestRunResultWorkflow.cs b/src/TestLogger/VSTest/Core/VSTestTestRunResultWorkflow.cs new file mode 100644 index 00000000..da85df22 --- /dev/null +++ b/src/TestLogger/VSTest/Core/VSTestTestRunResultWorkflow.cs @@ -0,0 +1,85 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.VSTest.Core +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Spekt.TestLogger.Core; + using Spekt.TestLogger.VSTest.Utilities; + using VSTestOutcome = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome; + using VSTestTestCase = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestCase; + using VSTestTestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult; + + public static class VSTestTestRunResultWorkflow + { + private static TestCaseNameParser parser; + private static LegacyTestCaseNameParser legacyParser; + + public static void Result(this ITestRun testRun, VSTestTestResult result) + { + parser ??= new TestCaseNameParser(testRun.ConsoleOutput); + legacyParser ??= new LegacyTestCaseNameParser(testRun.ConsoleOutput); + + var fqn = result.TestCase.FullyQualifiedName; + testRun.LoggerConfiguration.Values.TryGetValue(LoggerConfiguration.ParserKey, out string parserVal); + var parsedName = parserVal switch + { + string x when x.Equals("Legacy", StringComparison.OrdinalIgnoreCase) => legacyParser.Parse(fqn), + _ => parser.Parse(fqn), + }; + + var attachments = result.Attachments.SelectMany(x => x.ToAttachments(baseDirectory: TestRunResultWorkflow.GetTestResultDirectory(testRun), makeRelativePaths: testRun.LoggerConfiguration.UseRelativeAttachmentPaths)).ToList(); + + Func sanitize = testRun.Serializer.InputSanitizer.Sanitize; + + var traits = GetTraits(result.TestCase, sanitize); + var coreOutcome = (TestOutcome)(int)result.Outcome; + var resultInfo = new TestResultInfo( + sanitize(parsedName.Namespace), + sanitize(parsedName.Type), + sanitize(parsedName.Method), + sanitize(fqn), + coreOutcome, + sanitize(result.DisplayName), + sanitize(result.TestCase.DisplayName), + sanitize(result.TestCase.Source), + sanitize(result.TestCase.CodeFilePath), + result.TestCase.LineNumber, + result.StartTime.UtcDateTime, + result.EndTime.UtcDateTime, + result.Duration, + sanitize(result.ErrorMessage), + sanitize(result.ErrorStackTrace), + result.Messages.Select(x => new TestResultMessage(sanitize(x.Category), sanitize(x.Text))).ToList(), + attachments, + traits, + result.TestCase.ExecutorUri?.ToString()); + + // Apply framework-specific transformations using the real VSTest TestCase. + // Use per-result inline transformation so properties are extracted from the real ObjectModel TestCase. + var executorUri = result.TestCase.ExecutorUri?.ToString(); + var adapter = new Spekt.TestLogger.VSTest.Extensions.TestAdapterFactory().CreateTestAdapter(executorUri); + var transformed = adapter.TransformResults(new List { resultInfo }, new List(), result.TestCase); + testRun.Store.Add(transformed[0]); + } + + private static List GetTraits(VSTestTestCase testCase, Func sanitize) + { + var traits = new List(); + var traitProperty = testCase.Properties.FirstOrDefault(p => p.Id == "TestObject.Traits"); + if (traitProperty != null) + { + var traitValues = testCase.GetPropertyValue(traitProperty, Enumerable.Empty>()); + + foreach (var kvp in traitValues) + { + traits.Add(new Trait(sanitize(kvp.Key), sanitize(kvp.Value))); + } + } + + return traits; + } + } +} diff --git a/src/TestLogger/VSTest/Core/VSTestTestRunStartWorkflow.cs b/src/TestLogger/VSTest/Core/VSTestTestRunStartWorkflow.cs new file mode 100644 index 00000000..a7f5edf7 --- /dev/null +++ b/src/TestLogger/VSTest/Core/VSTestTestRunStartWorkflow.cs @@ -0,0 +1,25 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.VSTest.Core +{ + using System.Linq; + using System.Xml; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Spekt.TestLogger.Core; + + public static class VSTestTestRunStartWorkflow + { + public static TestRunConfiguration Start(this ITestRun testRun, TestRunStartEventArgs startedEvent) + { + var assemblyPath = startedEvent.TestRunCriteria.Sources.First(); + var runSettings = new XmlDocument(); + runSettings.LoadXml(startedEvent.TestRunCriteria.TestRunSettings); + var framework = runSettings + .GetElementsByTagName("TargetFrameworkVersion")[0] + .InnerText; + + return testRun.Start(assemblyPath, framework); + } + } +} diff --git a/src/TestLogger/Extensions/DefaultTestAdapter.cs b/src/TestLogger/VSTest/Extensions/DefaultTestAdapter.cs similarity index 77% rename from src/TestLogger/Extensions/DefaultTestAdapter.cs rename to src/TestLogger/VSTest/Extensions/DefaultTestAdapter.cs index 760a8ea8..a3c250d8 100644 --- a/src/TestLogger/Extensions/DefaultTestAdapter.cs +++ b/src/TestLogger/VSTest/Extensions/DefaultTestAdapter.cs @@ -1,14 +1,14 @@ // Copyright (c) Spekt Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Spekt.TestLogger.Extensions +namespace Spekt.TestLogger.VSTest.Extensions { using System.Collections.Generic; using Spekt.TestLogger.Core; public class DefaultTestAdapter : ITestAdapter { - public List TransformResults(List results, List messages) + public List TransformResults(List results, List messages, object testCase = null) { return results; } diff --git a/src/TestLogger/Extensions/ITestAdapter.cs b/src/TestLogger/VSTest/Extensions/ITestAdapter.cs similarity index 74% rename from src/TestLogger/Extensions/ITestAdapter.cs rename to src/TestLogger/VSTest/Extensions/ITestAdapter.cs index 2c920259..8718ffd9 100644 --- a/src/TestLogger/Extensions/ITestAdapter.cs +++ b/src/TestLogger/VSTest/Extensions/ITestAdapter.cs @@ -1,7 +1,7 @@ // Copyright (c) Spekt Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Spekt.TestLogger.Extensions +namespace Spekt.TestLogger.VSTest.Extensions { using System.Collections.Generic; using Spekt.TestLogger.Core; @@ -10,6 +10,7 @@ public interface ITestAdapter { List TransformResults( List results, - List messages); + List messages, + object testCase = null); } } \ No newline at end of file diff --git a/src/TestLogger/Extensions/ITestAdapterFactory.cs b/src/TestLogger/VSTest/Extensions/ITestAdapterFactory.cs similarity index 86% rename from src/TestLogger/Extensions/ITestAdapterFactory.cs rename to src/TestLogger/VSTest/Extensions/ITestAdapterFactory.cs index cfdc83b6..2a9b94f8 100644 --- a/src/TestLogger/Extensions/ITestAdapterFactory.cs +++ b/src/TestLogger/VSTest/Extensions/ITestAdapterFactory.cs @@ -1,7 +1,7 @@ // Copyright (c) Spekt Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Spekt.TestLogger.Extensions +namespace Spekt.TestLogger.VSTest.Extensions { public interface ITestAdapterFactory { diff --git a/src/TestLogger/Extensions/MSTestAdapter.cs b/src/TestLogger/VSTest/Extensions/MSTestAdapter.cs similarity index 56% rename from src/TestLogger/Extensions/MSTestAdapter.cs rename to src/TestLogger/VSTest/Extensions/MSTestAdapter.cs index 09bd2e53..34f69442 100644 --- a/src/TestLogger/Extensions/MSTestAdapter.cs +++ b/src/TestLogger/VSTest/Extensions/MSTestAdapter.cs @@ -1,14 +1,14 @@ // Copyright (c) Spekt Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Spekt.TestLogger.Extensions +namespace Spekt.TestLogger.VSTest.Extensions { using System.Collections.Generic; using Spekt.TestLogger.Core; public class MSTestAdapter : ITestAdapter { - public List TransformResults(List results, List messages) + public List TransformResults(List results, List messages, object testCase = null) { // MS Test puts test parameters in the DisplayName and not in the FullyQualifiedName. // So we use the DisplayName whenever it is available. @@ -26,27 +26,36 @@ public List TransformResults(List results, List< result.Method = displayName; } - CreateProperties(result); + CreateProperties(result, testCase); } return results; } - private static void CreateProperties(TestResultInfo result) + private static void CreateProperties(TestResultInfo result, object testCaseObj) { - if (result.TestCase is not { } testCase) + if (testCaseObj == null) + { + return; + } + + // Use reflection to avoid hard reference to Microsoft.TestPlatform.ObjectModel in Core assembly. + var testCaseType = testCaseObj.GetType(); + var propertiesProp = testCaseType.GetProperty("Properties"); + var propertiesValue = propertiesProp?.GetValue(testCaseObj) as System.Collections.IEnumerable; + if (propertiesValue == null) { return; } - // Parse property traits var properties = new List>(); - foreach (var property in testCase.Properties) + foreach (var property in propertiesValue) { - switch (property.Id) + var id = property.GetType().GetProperty("Id")?.GetValue(property) as string; + switch (id) { case "Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestProperty": - var propertyValue = testCase.GetPropertyValue(property) as string[]; + var propertyValue = InvokeGetPropertyValue(testCaseObj, property) as string[]; properties.Add(new KeyValuePair("CustomProperty", propertyValue)); break; } @@ -54,5 +63,11 @@ private static void CreateProperties(TestResultInfo result) result.Properties = properties; } + + private static object InvokeGetPropertyValue(object testCase, object property) + { + var method = testCase.GetType().GetMethod("GetPropertyValue", new[] { property.GetType() }); + return method?.Invoke(testCase, new[] { property }); + } } } diff --git a/src/TestLogger/Extensions/NUnitTestAdapter.cs b/src/TestLogger/VSTest/Extensions/NUnitTestAdapter.cs similarity index 53% rename from src/TestLogger/Extensions/NUnitTestAdapter.cs rename to src/TestLogger/VSTest/Extensions/NUnitTestAdapter.cs index f484b6ce..e3247c98 100644 --- a/src/TestLogger/Extensions/NUnitTestAdapter.cs +++ b/src/TestLogger/VSTest/Extensions/NUnitTestAdapter.cs @@ -1,7 +1,7 @@ // Copyright (c) Spekt Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Spekt.TestLogger.Extensions +namespace Spekt.TestLogger.VSTest.Extensions { using System; using System.Collections.Generic; @@ -12,50 +12,64 @@ public class NUnitTestAdapter : ITestAdapter { private const string ExplicitLabel = "Explicit"; - public List TransformResults(List results, List messages) + public List TransformResults(List results, List messages, object testCase = null) { foreach (var result in results) { // Mark tests with Explicit attribute as Skipped instead of Inconclusive. Explicit // is passed as a trait in the test platform. NUnit explicit attribute spec: // https://docs.nunit.org/articles/nunit/writing-tests/attributes/explicit.html - if (result.Outcome == Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.None && + if (result.Outcome == TestOutcome.None && result.Traits.Any(trait => trait.Name.Equals(ExplicitLabel, StringComparison.OrdinalIgnoreCase))) { - result.Outcome = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome.Skipped; + result.Outcome = TestOutcome.Skipped; } - CreateProperties(result); + CreateProperties(result, testCase); } return results; } - private static void CreateProperties(TestResultInfo result) + private static void CreateProperties(TestResultInfo result, object testCaseObj) { - if (result.TestCase is not { } testCase) + if (testCaseObj == null) + { + return; + } + + // Use reflection to avoid hard reference to Microsoft.TestPlatform.ObjectModel in Core assembly. + var testCaseType = testCaseObj.GetType(); + var propertiesProp = testCaseType.GetProperty("Properties"); + var propertiesValue = propertiesProp?.GetValue(testCaseObj) as System.Collections.IEnumerable; + if (propertiesValue == null) { return; } - // NUnit adapter uses Seed and TestCategory in TestCase Properties. - // Populate these properties if available. var properties = new List>(); - foreach (var property in testCase.Properties) + foreach (var property in propertiesValue) { - switch (property.Id) + var id = property.GetType().GetProperty("Id")?.GetValue(property) as string; + switch (id) { case "NUnit.Seed": case "NUnit.TestCategory": - properties.Add(new KeyValuePair(property.Id, testCase.GetPropertyValue(property))); + properties.Add(new KeyValuePair(id, InvokeGetPropertyValue(testCaseObj, property))); break; case "NUnit.Category": - properties.Add(new KeyValuePair("CustomProperty", testCase.GetPropertyValue(property))); + properties.Add(new KeyValuePair("CustomProperty", InvokeGetPropertyValue(testCaseObj, property))); break; } } result.Properties = properties; } + + private static object InvokeGetPropertyValue(object testCase, object property) + { + var method = testCase.GetType().GetMethod("GetPropertyValue", new[] { property.GetType() }); + return method?.Invoke(testCase, new[] { property }); + } } } diff --git a/src/TestLogger/Extensions/TestAdapterFactory.cs b/src/TestLogger/VSTest/Extensions/TestAdapterFactory.cs similarity index 95% rename from src/TestLogger/Extensions/TestAdapterFactory.cs rename to src/TestLogger/VSTest/Extensions/TestAdapterFactory.cs index 6153f3dc..3d8a2342 100644 --- a/src/TestLogger/Extensions/TestAdapterFactory.cs +++ b/src/TestLogger/VSTest/Extensions/TestAdapterFactory.cs @@ -1,7 +1,7 @@ // Copyright (c) Spekt Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Spekt.TestLogger.Extensions +namespace Spekt.TestLogger.VSTest.Extensions { public class TestAdapterFactory : ITestAdapterFactory { diff --git a/src/TestLogger/Extensions/XunitTestAdapter.cs b/src/TestLogger/VSTest/Extensions/XunitTestAdapter.cs similarity index 53% rename from src/TestLogger/Extensions/XunitTestAdapter.cs rename to src/TestLogger/VSTest/Extensions/XunitTestAdapter.cs index 58547308..41999eaf 100644 --- a/src/TestLogger/Extensions/XunitTestAdapter.cs +++ b/src/TestLogger/VSTest/Extensions/XunitTestAdapter.cs @@ -1,17 +1,21 @@ // Copyright (c) Spekt Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Spekt.TestLogger.Extensions +namespace Spekt.TestLogger.VSTest.Extensions { + using System; using System.Collections.Generic; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using System.Linq; using Spekt.TestLogger.Core; public class XunitTestAdapter : ITestAdapter { + private const string SkipReasonCategory = "skipReason"; + public List TransformResults( List results, - List messages) + List messages, + object testCase = null) { var transformedResults = new List(); @@ -43,7 +47,21 @@ public List TransformResults( if (skippedTestNamesWithReason.TryGetValue(result.TestCaseDisplayName, out var skipReason)) { // TODO: Defining a new category for now... - result.Messages.Add(new TestResultMessage("skipReason", skipReason)); + result.Messages.Add(new TestResultMessage(SkipReasonCategory, skipReason)); + } + else if (result.Outcome == TestOutcome.Skipped) + { + // Fallback: run level skip messages are not available when transforming + // per result, so derive the reason from the result's own StandardOut + // message, if any. Skipped tests never execute, so their StandardOut + // carries only runner-generated text (the skip reason). + var reason = result.Messages + .FirstOrDefault(m => TestResultMessage.StandardOutCategory.Equals(m.Category, StringComparison.OrdinalIgnoreCase) + && !string.IsNullOrWhiteSpace(m.Text))?.Text.Trim(); + if (!string.IsNullOrEmpty(reason)) + { + result.Messages.Add(new TestResultMessage(SkipReasonCategory, reason)); + } } string displayName = result.TestResultDisplayName; @@ -56,7 +74,7 @@ public List TransformResults( result.Method += displayName.Substring(i); } - CreateProperties(result); + CreateProperties(result, testCase); transformedResults.Add(result); } @@ -64,9 +82,18 @@ public List TransformResults( return transformedResults; } - private static void CreateProperties(TestResultInfo result) + private static void CreateProperties(TestResultInfo result, object testCaseObj) { - if (result.TestCase is not { } testCase) + if (testCaseObj == null) + { + return; + } + + // Use reflection to avoid hard reference to Microsoft.TestPlatform.ObjectModel in Core assembly. + var testCaseType = testCaseObj.GetType(); + var propertiesProp = testCaseType.GetProperty("Properties"); + var propertiesValue = propertiesProp?.GetValue(testCaseObj) as System.Collections.IEnumerable; + if (propertiesValue == null) { return; } @@ -74,13 +101,13 @@ private static void CreateProperties(TestResultInfo result) var properties = new List>(); // Parse test traits via Trait decorator - foreach (var property in result.TestCase.Properties) + foreach (var property in propertiesValue) { - switch (property.Id) + var id = property.GetType().GetProperty("Id")?.GetValue(property) as string; + switch (id) { case "Xunit.Trait": - var propertyValue = result.TestCase.GetPropertyValue(property) as string[]; - + var propertyValue = InvokeGetPropertyValue(testCaseObj, property) as string[]; properties.Add(new KeyValuePair("CustomProperty", propertyValue)); break; } @@ -88,5 +115,11 @@ private static void CreateProperties(TestResultInfo result) result.Properties = properties; } + + private static object InvokeGetPropertyValue(object testCase, object property) + { + var method = testCase.GetType().GetMethod("GetPropertyValue", new[] { property.GetType() }); + return method?.Invoke(testCase, new[] { property }); + } } } \ No newline at end of file diff --git a/src/TestLogger/TestLogger.cs b/src/TestLogger/VSTest/TestLogger.cs similarity index 94% rename from src/TestLogger/TestLogger.cs rename to src/TestLogger/VSTest/TestLogger.cs index 69789999..12e5a5fc 100644 --- a/src/TestLogger/TestLogger.cs +++ b/src/TestLogger/VSTest/TestLogger.cs @@ -1,7 +1,7 @@ // Copyright (c) Spekt Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Spekt.TestLogger +namespace Spekt.TestLogger.VSTest { using System; using System.Collections.Generic; @@ -10,11 +10,11 @@ namespace Spekt.TestLogger using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Spekt.TestLogger.Core; using Spekt.TestLogger.Platform; + using Spekt.TestLogger.VSTest.Core; /// /// Base test logger implementation. /// This implementation is VSTest-specific. - /// The Microsoft.Testing.Platform implementation is . /// public abstract class TestLogger : ITestLoggerWithParameters { @@ -59,7 +59,7 @@ public void Initialize(TestLoggerEvents events, string testResultsDirPath) var config = new Dictionary { - { DefaultLoggerParameterNames.TestRunDirectory, testResultsDirPath }, + { Spekt.TestLogger.Core.DefaultLoggerParameterNames.TestRunDirectory, testResultsDirPath }, { LoggerConfiguration.LogFilePathKey, Path.Combine(testResultsDirPath, this.DefaultTestResultFile) } }; @@ -102,8 +102,9 @@ private void CreateTestRun(TestLoggerEvents events, LoggerConfiguration config) .WithConsoleOutput(this.consoleOutput) .WithStore(this.resultStore) .WithSerializer(this.resultSerializer) - .Subscribe(events) .Build(); + + this.testRun.Subscribe(events); } } } diff --git a/src/TestLogger/VSTest/Utilities/AttachmentSetExtensions.cs b/src/TestLogger/VSTest/Utilities/AttachmentSetExtensions.cs new file mode 100644 index 00000000..09d5e23b --- /dev/null +++ b/src/TestLogger/VSTest/Utilities/AttachmentSetExtensions.cs @@ -0,0 +1,65 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.VSTest.Utilities +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Spekt.TestLogger.Core; + + public static class AttachmentSetExtensions + { + public static IEnumerable ToAttachments(this Microsoft.VisualStudio.TestPlatform.ObjectModel.AttachmentSet attachmentSet, string baseDirectory, bool makeRelativePaths) + { + if (makeRelativePaths && !string.IsNullOrEmpty(baseDirectory)) + { + return attachmentSet.Attachments.Select(a => + { + var attachmentPath = GetPathFromUri(a.Uri); + var relativePath = MakeRelativePath(baseDirectory, attachmentPath); + return new TestAttachmentInfo(relativePath, a.Description); + }); + } + + return attachmentSet.Attachments.Select(a => new + TestAttachmentInfo(GetPathFromUri(a.Uri), a.Description)); + } + + private static string MakeRelativePath(string baseDirectoryPath, string targetPath) + { + if (!baseDirectoryPath.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()) && + !baseDirectoryPath.EndsWith(System.IO.Path.AltDirectorySeparatorChar.ToString())) + { + baseDirectoryPath += System.IO.Path.DirectorySeparatorChar; + } + + if (!System.IO.Path.IsPathRooted(targetPath) || + !string.Equals(System.IO.Path.GetPathRoot(baseDirectoryPath), System.IO.Path.GetPathRoot(targetPath), System.StringComparison.OrdinalIgnoreCase)) + { + return targetPath; + } + + var baseUri = new System.Uri(baseDirectoryPath); + var targetUri = new System.Uri(targetPath); + + var relativeUri = baseUri.MakeRelativeUri(targetUri); + var relativePath = System.Uri.UnescapeDataString(relativeUri.ToString()); + + return relativePath.Replace('/', System.IO.Path.DirectorySeparatorChar); + } + + private static string GetPathFromUri(Uri uri) + { + try + { + return uri.LocalPath; + } + catch (InvalidOperationException) + { + return uri.OriginalString; + } + } + } +} \ No newline at end of file diff --git a/src/Xunit.Xml.Package/Xunit.Xml.Package.csproj b/src/Xunit.Xml.Package/Xunit.Xml.Package.csproj index 626708cb..22a00e0a 100644 --- a/src/Xunit.Xml.Package/Xunit.Xml.Package.csproj +++ b/src/Xunit.Xml.Package/Xunit.Xml.Package.csproj @@ -35,9 +35,13 @@ packageIcon.png - - - + + + + + + + @@ -65,6 +69,15 @@ + + + + + + + + + diff --git a/src/Xunit.Xml.Package/XunitXml.TestLogger.nuspec b/src/Xunit.Xml.Package/XunitXml.TestLogger.nuspec index 9d292371..4f13073a 100644 --- a/src/Xunit.Xml.Package/XunitXml.TestLogger.nuspec +++ b/src/Xunit.Xml.Package/XunitXml.TestLogger.nuspec @@ -13,12 +13,14 @@ packageIcon.png - - + + + + - + diff --git a/src/Xunit.Xml.Package/XunitXml.TestLogger.props b/src/Xunit.Xml.Package/XunitXml.TestLogger.props index d0c0978d..db018857 100644 --- a/src/Xunit.Xml.Package/XunitXml.TestLogger.props +++ b/src/Xunit.Xml.Package/XunitXml.TestLogger.props @@ -1,20 +1,20 @@ - - Microsoft.VisualStudio.TestPlatform.Extension.Xunit.Xml.TestLogger.dll + + Spekt.TestLogger.VSTest.dll PreserveNewest False - - Spekt.TestLogger.dll + + Microsoft.VisualStudio.TestPlatform.Extension.Xunit.Xml.TestLogger.dll PreserveNewest False - - + + XunitXml.TestLogger diff --git a/src/Xunit.Xml.TestLogger/Assembly.cs b/src/Xunit.Xml.TestLogger/Assembly.cs index fc946f36..e8eeabf0 100644 --- a/src/Xunit.Xml.TestLogger/Assembly.cs +++ b/src/Xunit.Xml.TestLogger/Assembly.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; +#if !DEBUG [assembly: ExcludeFromCodeCoverage] namespace System.Diagnostics.CodeAnalysis; @@ -16,3 +17,4 @@ internal sealed class ExcludeFromCodeCoverageAttribute : Attribute { public ExcludeFromCodeCoverageAttribute() { } } +#endif diff --git a/src/Xunit.Xml.TestLogger/Xunit.Xml.TestLogger.MTP.csproj b/src/Xunit.Xml.TestLogger/Xunit.Xml.TestLogger.MTP.csproj new file mode 100644 index 00000000..4c5a4930 --- /dev/null +++ b/src/Xunit.Xml.TestLogger/Xunit.Xml.TestLogger.MTP.csproj @@ -0,0 +1,29 @@ + + + + netstandard2.0 + Microsoft.VisualStudio.TestPlatform.Extension.Xunit.Xml.TestLogger + $(PackageVersion) + false + false + true + false + + + + Microsoft.VisualStudio.TestPlatform.Extension.Xunit.Xml.TestLogger + + + + + + + + + + + + + + + diff --git a/src/Xunit.Xml.TestLogger/Xunit.Xml.TestLogger.csproj b/src/Xunit.Xml.TestLogger/Xunit.Xml.TestLogger.VSTest.csproj similarity index 60% rename from src/Xunit.Xml.TestLogger/Xunit.Xml.TestLogger.csproj rename to src/Xunit.Xml.TestLogger/Xunit.Xml.TestLogger.VSTest.csproj index 51a96e00..32297884 100644 --- a/src/Xunit.Xml.TestLogger/Xunit.Xml.TestLogger.csproj +++ b/src/Xunit.Xml.TestLogger/Xunit.Xml.TestLogger.VSTest.csproj @@ -5,6 +5,7 @@ Microsoft.VisualStudio.TestPlatform.Extension.Xunit.Xml.TestLogger $(PackageVersion) false + false @@ -12,11 +13,18 @@ - + - + + + + + + + + diff --git a/src/Xunit.Xml.TestLogger/XunitTestReporter.cs b/src/Xunit.Xml.TestLogger/XunitTestReporter.cs index bc96585d..ad617f6e 100644 --- a/src/Xunit.Xml.TestLogger/XunitTestReporter.cs +++ b/src/Xunit.Xml.TestLogger/XunitTestReporter.cs @@ -6,6 +6,7 @@ namespace Spekt.TestReporter.Xunit using System; using Microsoft.VisualStudio.TestPlatform.Extension.Xunit.Xml.TestLogger; using Spekt.TestLogger.Core; + using Spekt.TestLogger.Mtp; internal sealed class XunitTestReporter : TestReporter { diff --git a/src/Xunit.Xml.TestLogger/XunitTestReporterExtensions.cs b/src/Xunit.Xml.TestLogger/XunitTestReporterExtensions.cs index 98d42765..e89cfdd0 100644 --- a/src/Xunit.Xml.TestLogger/XunitTestReporterExtensions.cs +++ b/src/Xunit.Xml.TestLogger/XunitTestReporterExtensions.cs @@ -5,6 +5,7 @@ namespace Spekt.TestReporter.Xunit { using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.Extensions; + using Spekt.TestLogger.Mtp; public static class XunitTestReporterExtensions { diff --git a/src/Xunit.Xml.TestLogger/XunitXmlSerializer.cs b/src/Xunit.Xml.TestLogger/XunitXmlSerializer.cs index 5a850361..2583fc52 100644 --- a/src/Xunit.Xml.TestLogger/XunitXmlSerializer.cs +++ b/src/Xunit.Xml.TestLogger/XunitXmlSerializer.cs @@ -10,7 +10,6 @@ namespace Microsoft.VisualStudio.TestPlatform.Extension.Xunit.Xml.TestLogger using System.Linq; using System.Text; using System.Xml.Linq; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Spekt.TestLogger.Core; public class XunitXmlSerializer : ITestResultSerializer diff --git a/src/Xunit.Xml.TestLogger/XunitXmlTestLogger.cs b/src/Xunit.Xml.TestLogger/XunitXmlTestLogger.cs index 299e6c3f..e69a157d 100644 --- a/src/Xunit.Xml.TestLogger/XunitXmlTestLogger.cs +++ b/src/Xunit.Xml.TestLogger/XunitXmlTestLogger.cs @@ -4,7 +4,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Extension.Xunit.Xml.TestLogger { using Microsoft.VisualStudio.TestPlatform.ObjectModel; - using Spekt.TestLogger; + using Spekt.TestLogger.VSTest; [FriendlyName(FriendlyName)] [ExtensionUri(ExtensionUri)] diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnit.Xml.TestLogger.AcceptanceTests.csproj b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnit.Xml.TestLogger.AcceptanceTests.csproj index 4299bab8..c07d632b 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnit.Xml.TestLogger.AcceptanceTests.csproj +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnit.Xml.TestLogger.AcceptanceTests.csproj @@ -10,7 +10,7 @@ - + @@ -28,15 +28,21 @@ + + + + + - + + - + diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerAcceptanceTests.cs b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerAcceptanceTests.cs index c7f790e6..4f886d8f 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerAcceptanceTests.cs +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerAcceptanceTests.cs @@ -31,14 +31,13 @@ public static void SuiteInitialize(TestContext context) var vstestLoggerArgs = $"junit;LogFilePath={VstestResultsFile}"; _ = DotnetTestFixture .Create() - .WithBuild() .Execute(AssetName, vstestLoggerArgs, collectCoverage: false, VstestResultsFile, isMTP: false); // Run MTP tests var mtpLoggerArgs = $"--report-spekt-junit --report-spekt-junit-filename {MtpResultsFile}"; _ = DotnetTestFixture .Create() - .WithBuild() + .WithNoBuild() .Execute(AssetName, mtpLoggerArgs, collectCoverage: false, MtpResultsFile, isMTP: true); } diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerFormatOptionsAcceptanceTests.cs b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerFormatOptionsAcceptanceTests.cs index 977d5636..88390e64 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerFormatOptionsAcceptanceTests.cs +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerFormatOptionsAcceptanceTests.cs @@ -33,7 +33,6 @@ public void FailureBodyFormat_Default_ShouldntStartWithMessage() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "failure-default-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -61,7 +60,6 @@ public void FailureBodyFormat_Verbose_ShouldNotContainConsoleOut() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "failure-verbose-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -81,7 +79,6 @@ public void FailureBodyFormat_Verbose_ShouldStartWithMessage() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "failure-verbose-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -110,7 +107,6 @@ public void FailureBodyFormat_Verbose_ShouldContainConsoleOut() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "failure-verbose-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -130,7 +126,6 @@ public void MethodFormat_Default_ShouldBeOnlyTheMethod() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "method-default-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -156,7 +151,6 @@ public void MethodFormat_Class_ShouldIncludeClass() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "method-class-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -184,7 +178,6 @@ public void MethodFormat_Full_ShouldIncludeNamespaceAndClass() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "method-full-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerNetFullAcceptanceTests.cs b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerNetFullAcceptanceTests.cs index 3b82b12b..a680434d 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerNetFullAcceptanceTests.cs +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerNetFullAcceptanceTests.cs @@ -42,7 +42,6 @@ public static void SuiteInitialize(TestContext context) // Enable reporting of internal properties in the adapter using runsettings _ = DotnetTestFixture .Create() - .WithBuild() .Execute(AssetName, loggerArgs, collectCoverage: false, "test-results.xml"); } diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerPathTests.cs b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerPathTests.cs index 1687b8d7..2da0b88d 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerPathTests.cs +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerPathTests.cs @@ -36,7 +36,6 @@ public void TestRunWithLoggerAndFilePathShouldCreateResultsFile() _ = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetMulti.Tests", loggerArgs, collectCoverage: false, "test-results.xml"); foreach (string resultFile in testResultFiles) diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerResultDirectoryAcceptanceTests.cs b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerResultDirectoryAcceptanceTests.cs index 06a4e570..c0a84ba0 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerResultDirectoryAcceptanceTests.cs +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerResultDirectoryAcceptanceTests.cs @@ -23,7 +23,6 @@ public void TestRunWithResultDirectoryAndFileNameShouldCreateResultsFile() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .WithResultsDirectory("artifacts") .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "test-results.xml"); diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerStoreConsoleOutputOptionsAcceptanceTests.cs b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerStoreConsoleOutputOptionsAcceptanceTests.cs index c29c4206..6a2a5dbd 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerStoreConsoleOutputOptionsAcceptanceTests.cs +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerStoreConsoleOutputOptionsAcceptanceTests.cs @@ -26,7 +26,6 @@ public void StoreConsoleOutput_Default_ContainsConsoleOut() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "output-default-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -51,7 +50,6 @@ public void StoreConsoleOutput_Default_ContainsConsoleErr() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "output-default-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -72,7 +70,6 @@ public void StoreConsoleOutput_True_ContainsConsoleOut() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "output-true-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -97,7 +94,6 @@ public void StoreConsoleOutput_True_ContainsConsoleErr() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "output-true-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -118,7 +114,6 @@ public void StoreConsoleOutput_False_DoesNotContainConsoleOut() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "output-false-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -140,7 +135,6 @@ public void StoreConsoleOutput_False_DoesNotContainConsoleErr() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "output-false-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -159,7 +153,6 @@ public void StoreConsoleOutput_TestSuite_ContainsConsoleOutOnlyForTestSuite() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "output-testsuite-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -184,7 +177,6 @@ public void StoreConsoleOutput_TestSuite_ContainsConsoleErrOnlyForTestSuite() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "output-testsuite-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -204,7 +196,6 @@ public void StoreConsoleOutput_TestCase_ContainsConsoleOutOnlyForTestCase() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "output-testcase-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); @@ -226,7 +217,6 @@ public void StoreConsoleOutput_TestCase_ContainsConsoleErrOnlyForTestCase() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .Execute("JUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "output-testcase-test-results.xml"); XDocument resultsXml = XDocument.Load(resultsFile); diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerXUnitAcceptanceTests.cs b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerXUnitAcceptanceTests.cs index 1e91a85c..c569c3b8 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerXUnitAcceptanceTests.cs +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerXUnitAcceptanceTests.cs @@ -33,14 +33,13 @@ public static void SuiteInitialize(TestContext context) var vstestLoggerArgs = $"junit;LogFilePath={VstestResultsFile}"; _ = DotnetTestFixture .Create() - .WithBuild() .Execute(AssetName, vstestLoggerArgs, collectCoverage: false, VstestResultsFile, isMTP: false); // Run MTP tests var mtpLoggerArgs = $"--report-spekt-junit --report-spekt-junit-filename {MtpResultsFile}"; _ = DotnetTestFixture .Create() - .WithBuild() + .WithNoBuild() .Execute(AssetName, mtpLoggerArgs, collectCoverage: false, MtpResultsFile, isMTP: true); } diff --git a/test/JUnit.Xml.TestLogger.UnitTests/JUnit.Xml.TestLogger.UnitTests.csproj b/test/JUnit.Xml.TestLogger.UnitTests/JUnit.Xml.TestLogger.UnitTests.csproj index 58162069..7608818e 100644 --- a/test/JUnit.Xml.TestLogger.UnitTests/JUnit.Xml.TestLogger.UnitTests.csproj +++ b/test/JUnit.Xml.TestLogger.UnitTests/JUnit.Xml.TestLogger.UnitTests.csproj @@ -6,6 +6,9 @@ true false true + Exe + true + true @@ -13,12 +16,17 @@ + + + + + - - + + diff --git a/test/JUnit.Xml.TestLogger.UnitTests/JUnitXmlTestSerializerTests.cs b/test/JUnit.Xml.TestLogger.UnitTests/JUnitXmlTestSerializerTests.cs index 9bc6e252..7ae46bd6 100644 --- a/test/JUnit.Xml.TestLogger.UnitTests/JUnitXmlTestSerializerTests.cs +++ b/test/JUnit.Xml.TestLogger.UnitTests/JUnitXmlTestSerializerTests.cs @@ -16,7 +16,11 @@ namespace JUnit.Xml.TestLogger.UnitTests using Microsoft.VisualStudio.TestTools.UnitTesting; using Spekt.TestLogger.Core; using Spekt.TestLogger.Utilities; + using TestMessageLevel = Spekt.TestLogger.Core.TestMessageLevel; + using TestOutcome = Spekt.TestLogger.Core.TestOutcome; + using TestResultMessage = Spekt.TestLogger.Core.TestResultMessage; using TestSuite = Microsoft.VisualStudio.TestPlatform.Extension.Junit.Xml.TestLogger.JunitXmlSerializer.TestSuite; + using Trait = Spekt.TestLogger.Core.Trait; [TestClass] public class JUnitXmlTestSerializerTests @@ -296,8 +300,7 @@ private static TestResultInfo CreateTestResultInfo( messages ?? new List(), new List(), new List(), - "executor://dummy", - null); + "executor://dummy"); } private static XElement SerializeAndGetTestCaseElement(JunitXmlSerializer serializer, TestResultInfo result) diff --git a/test/Json.TestLogger.Package/Json.TestLogger.nuspec b/test/Json.TestLogger.Package/Json.TestLogger.nuspec index 84439ff5..b088b523 100644 --- a/test/Json.TestLogger.Package/Json.TestLogger.nuspec +++ b/test/Json.TestLogger.Package/Json.TestLogger.nuspec @@ -13,7 +13,7 @@ - + diff --git a/test/Json.TestLogger.Package/Json.TestLogger.props b/test/Json.TestLogger.Package/Json.TestLogger.props index 66c37f09..c5a5e203 100644 --- a/test/Json.TestLogger.Package/Json.TestLogger.props +++ b/test/Json.TestLogger.Package/Json.TestLogger.props @@ -11,8 +11,8 @@ PreserveNewest False - - Spekt.TestLogger.dll + + Spekt.TestLogger.VSTest.dll PreserveNewest False diff --git a/test/Json.TestLogger.TestAdapter/Json.TestLogger.TestAdapter.csproj b/test/Json.TestLogger.TestAdapter/Json.TestLogger.TestAdapter.csproj index 781a7ced..9693d7b8 100644 --- a/test/Json.TestLogger.TestAdapter/Json.TestLogger.TestAdapter.csproj +++ b/test/Json.TestLogger.TestAdapter/Json.TestLogger.TestAdapter.csproj @@ -14,16 +14,16 @@ JsonTestLogger.cs - + JsonTestResultSerializer.cs - + InputSanitizerJson.cs - + diff --git a/test/Json.TestLogger/Json.TestLogger.csproj b/test/Json.TestLogger/Json.TestLogger.csproj index 13fda1a8..dceedcde 100644 --- a/test/Json.TestLogger/Json.TestLogger.csproj +++ b/test/Json.TestLogger/Json.TestLogger.csproj @@ -12,16 +12,16 @@ - + JsonTestResultSerializer.cs - + InputSanitizerJson.cs - + diff --git a/test/Json.TestLogger/JsonTestLogger.cs b/test/Json.TestLogger/JsonTestLogger.cs index 086c6fba..c8c32582 100644 --- a/test/Json.TestLogger/JsonTestLogger.cs +++ b/test/Json.TestLogger/JsonTestLogger.cs @@ -8,7 +8,7 @@ namespace Json.TestLogger [FriendlyName(FriendlyName)] [ExtensionUri(ExtensionUri)] - public class JsonTestLogger : Spekt.TestLogger.TestLogger + public class JsonTestLogger : Spekt.TestLogger.VSTest.TestLogger { /// /// Uri used to uniquely identify the logger. diff --git a/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnit.Xml.TestLogger.AcceptanceTests.csproj b/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnit.Xml.TestLogger.AcceptanceTests.csproj index b613546c..c99b2754 100644 --- a/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnit.Xml.TestLogger.AcceptanceTests.csproj +++ b/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnit.Xml.TestLogger.AcceptanceTests.csproj @@ -10,7 +10,7 @@ - + @@ -27,10 +27,15 @@ + + + + + diff --git a/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerAcceptanceTests.cs b/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerAcceptanceTests.cs index a3ce239a..65f55886 100644 --- a/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerAcceptanceTests.cs +++ b/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerAcceptanceTests.cs @@ -26,7 +26,6 @@ public static void SuiteInitialize(TestContext context) var vstestLoggerArgs = "nunit;LogFilePath=test-results-vstest.xml"; _ = DotnetTestFixture .Create() - .WithBuild() .WithRunSettings("-- NUnit.ShowInternalProperties=true") .Execute(AssetName, vstestLoggerArgs, collectCoverage: false, "test-results-vstest.xml", isMTP: false); @@ -34,8 +33,7 @@ public static void SuiteInitialize(TestContext context) var mtpLoggerArgs = "--report-spekt-nunit --report-spekt-nunit-filename test-results-mtp.xml"; _ = DotnetTestFixture .Create() - .WithBuild() - .WithRunSettings("NUnit.ShowInternalProperties=true") + .WithNoBuild() .Execute(AssetName, mtpLoggerArgs, collectCoverage: false, "test-results-mtp.xml", isMTP: true); } @@ -109,7 +107,7 @@ public void TestResultFileShouldContainAssemblyTestSuite(string resultFileName) Assert.AreEqual("Failed", node.Attribute(XName.Get("result")).Value); Assert.AreEqual("NUnit.Xml.TestLogger.NetCore.Tests.dll", node.Attribute(XName.Get("name")).Value); - Assert.AreEqual(AssetName.ToAssetAssemblyPath("net8.0"), node.Attribute(XName.Get("fullname")).Value); + Assert.AreEqual(AssetName.ToAssetAssemblyPath("net8.0", resultFileName.Contains("mtp")), node.Attribute(XName.Get("fullname")).Value); var startTimeStr = node.Attribute(XName.Get("start-time"))?.Value; var endTimeStr = node.Attribute(XName.Get("end-time"))?.Value; diff --git a/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerNetFullAcceptanceTests.cs b/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerNetFullAcceptanceTests.cs index 3bc4b801..3ed74e87 100644 --- a/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerNetFullAcceptanceTests.cs +++ b/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerNetFullAcceptanceTests.cs @@ -42,7 +42,6 @@ public static void SuiteInitialize(TestContext context) // Enable reporting of internal properties in the adapter using runsettings _ = DotnetTestFixture .Create() - .WithBuild() .Execute(AssetName, loggerArgs, collectCoverage: false, "test-results.xml"); } diff --git a/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerPathTests.cs b/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerPathTests.cs index d2e6a074..e28a4458 100644 --- a/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerPathTests.cs +++ b/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerPathTests.cs @@ -31,7 +31,6 @@ public void TestRunWithLoggerAndFilePathShouldCreateResultsFile() _ = DotnetTestFixture .Create() - .WithBuild() .Execute("NUnit.Xml.TestLogger.NetMulti.Tests", loggerArgs, collectCoverage: false, "test-results.xml"); foreach (string resultFile in testResultFiles) diff --git a/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerResultDirectoryAcceptanceTests.cs b/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerResultDirectoryAcceptanceTests.cs index 6617c250..974f2b79 100644 --- a/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerResultDirectoryAcceptanceTests.cs +++ b/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnitTestLoggerResultDirectoryAcceptanceTests.cs @@ -17,7 +17,6 @@ public void TestRunWithResultDirectoryAndFileNameShouldCreateResultsFile() var resultsFile = DotnetTestFixture .Create() - .WithBuild() .WithResultsDirectory("artifacts") .Execute("NUnit.Xml.TestLogger.NetCore.Tests", loggerArgs, collectCoverage: false, "test-results.xml"); diff --git a/test/NUnit.Xml.TestLogger.UnitTests/NUnit.Xml.TestLogger.UnitTests.csproj b/test/NUnit.Xml.TestLogger.UnitTests/NUnit.Xml.TestLogger.UnitTests.csproj index a28ce267..fc1fad39 100644 --- a/test/NUnit.Xml.TestLogger.UnitTests/NUnit.Xml.TestLogger.UnitTests.csproj +++ b/test/NUnit.Xml.TestLogger.UnitTests/NUnit.Xml.TestLogger.UnitTests.csproj @@ -6,6 +6,9 @@ true false true + Exe + true + true @@ -13,12 +16,17 @@ + + + + + - - + + diff --git a/test/TestLogger.AcceptanceTests/TestLogger.AcceptanceTests.csproj b/test/TestLogger.AcceptanceTests/TestLogger.AcceptanceTests.csproj index 78df04f8..695fbdfd 100644 --- a/test/TestLogger.AcceptanceTests/TestLogger.AcceptanceTests.csproj +++ b/test/TestLogger.AcceptanceTests/TestLogger.AcceptanceTests.csproj @@ -28,6 +28,18 @@ Always - + + + + + + + + + + + + + diff --git a/test/TestLogger.AcceptanceTests/TestLoggerAcceptanceTests.cs b/test/TestLogger.AcceptanceTests/TestLoggerAcceptanceTests.cs index 0c65d8b7..d6b6e985 100644 --- a/test/TestLogger.AcceptanceTests/TestLoggerAcceptanceTests.cs +++ b/test/TestLogger.AcceptanceTests/TestLoggerAcceptanceTests.cs @@ -57,6 +57,11 @@ private Task VerifyAssembly(string testAssembly, string loggerArgs, string addit var pathForwardSlashes = m.Groups[1].Captures[0].Value.Replace('\\', '/'); x = pathForwardSlashes; x = x.Replace("//", "/"); + + // Test runner flavor (vstest/mtp) is an implementation detail of the + // build layout; normalize it away so snapshots stay stable. + x = x.Replace("bin/Debug/vstest/", "bin/Debug/"); + x = x.Replace("bin/Debug/mtp/", "bin/Debug/"); } else if (prefixedMatch.IsMatch(x)) { @@ -78,7 +83,7 @@ private Task VerifyAssembly(string testAssembly, string loggerArgs, string addit // Collect coverage will attach a runlevel attachment. var collectCoverage = testAssembly.Contains("XUnit.NetCore"); - var resultsFile = DotnetTestFixture.Create().WithBuild().Execute(testAssembly, loggerArgs, collectCoverage, "test-results.json"); + var resultsFile = DotnetTestFixture.Create().Execute(testAssembly, loggerArgs, collectCoverage, "test-results.json"); var testReport = JsonConvert.DeserializeObject(File.ReadAllText(resultsFile)); // Using VerifyJson with serialized JSON to avoid incompatibility in object serialization diff --git a/test/TestLogger.Fixtures/DotnetTestFixture.cs b/test/TestLogger.Fixtures/DotnetTestFixture.cs index f06827b9..ecf71c4b 100644 --- a/test/TestLogger.Fixtures/DotnetTestFixture.cs +++ b/test/TestLogger.Fixtures/DotnetTestFixture.cs @@ -6,12 +6,13 @@ namespace TestLogger.Fixtures using System; using System.Diagnostics; using System.IO; + using System.Text; public class DotnetTestFixture { private const string NetcoreVersion = "net8.0"; - private bool buildProject = false; private bool cleanProject = false; + private bool noBuild = false; private string relativeResultsDirectory = string.Empty; private string runSettingsSuffix = string.Empty; @@ -20,7 +21,20 @@ public class DotnetTestFixture public DotnetTestFixture WithBuild(bool cleanProject = true) { this.cleanProject = cleanProject; - this.buildProject = true; + this.noBuild = false; + return this; + } + + /// + /// Runs the test leg with --no-build against pre-built outputs. + /// Only use where --no-build is known to work (MTP legs); VSTest legs + /// must build (incrementally) because vstest.console rejects --no-build runs. + /// + /// The current fixture instance. + public DotnetTestFixture WithNoBuild() + { + this.cleanProject = false; + this.noBuild = true; return this; } @@ -49,7 +63,7 @@ public string Execute(string assemblyName, string loggerArgs, bool collectCovera UseShellExecute = false, RedirectStandardOutput = true, FileName = "dotnet", - Arguments = $"clean \"{assemblyName.ToAssetDirectoryPath()}\\{assemblyName}.csproj\"" + Arguments = $"clean \"{assemblyName.ToAssetDirectoryPath()}\\{assemblyName}.csproj\"{(isMTP ? " -p:IsMTP=true" : string.Empty)}" } }; cleanProcess.Start(); @@ -72,8 +86,9 @@ public string Execute(string assemblyName, string loggerArgs, bool collectCovera File.Delete(resultsFile); } - // Run dotnet test with logger - var buildArgs = this.buildProject ? string.Empty : "--no-build"; + // Run dotnet test with logger. --no-build is opt-in (WithNoBuild) because + // vstest.console rejects --no-build runs; default is an incremental build. + var buildArgs = this.noBuild ? "--no-build" : string.Empty; var resultDirectoryArgs = string.IsNullOrEmpty(this.relativeResultsDirectory) ? string.Empty : $"--results-directory \"{resultsDirectory}\""; if (isMTP) @@ -120,17 +135,36 @@ public string Execute(string assemblyName, string loggerArgs, bool collectCovera dotnet.StartInfo.Arguments += $" --collect:\"XPlat Code Coverage\" --settings \"{coverletRunSettingsPath}\""; } - this.LogTestAssetOutDir(assemblyName); + this.LogTestAssetOutDir(assemblyName, isMTP); Console.WriteLine("\n\n## Test run arguments: dotnet " + dotnet.StartInfo.Arguments); - // To avoid deadlocks, always read the output stream first and then wait. + // Use async reads to avoid deadlock when child process writes heavily to stderr + // while parent blocks on stdout ReadToEnd(). + var outputBuilder = new StringBuilder(); + var errorBuilder = new StringBuilder(); + dotnet.OutputDataReceived += (sender, e) => + { + if (e.Data != null) + { + outputBuilder.AppendLine(e.Data); + } + }; + dotnet.ErrorDataReceived += (sender, e) => + { + if (e.Data != null) + { + errorBuilder.AppendLine(e.Data); + } + }; dotnet.Start(); - - var output = dotnet.StandardOutput.ReadToEnd(); - var error = dotnet.StandardError.ReadToEnd(); + dotnet.BeginOutputReadLine(); + dotnet.BeginErrorReadLine(); dotnet.WaitForExit(); + var output = outputBuilder.ToString(); + var error = errorBuilder.ToString(); + Console.WriteLine("\n\n ## Test run output\n" + output); if (!string.IsNullOrEmpty(error)) { @@ -140,14 +174,16 @@ public string Execute(string assemblyName, string loggerArgs, bool collectCovera return resultsFile; } - private void LogTestAssetOutDir(string assemblyName) + private void LogTestAssetOutDir(string assemblyName, bool isMTP) { // Log the contents of test output directory. Useful to verify if the logger is copied Console.WriteLine("\n\n## Contents of test output directory:"); + var flavor = isMTP ? "mtp" : "vstest"; + // Create directory so test does not fail under windows. - Directory.CreateDirectory(Path.Combine(assemblyName, $"bin/Debug/{NetcoreVersion}")); - foreach (var f in Directory.GetFiles(Path.Combine(assemblyName, $"bin/Debug/{NetcoreVersion}"))) + Directory.CreateDirectory(Path.Combine(assemblyName, $"bin/Debug/{flavor}/{NetcoreVersion}")); + foreach (var f in Directory.GetFiles(Path.Combine(assemblyName, $"bin/Debug/{flavor}/{NetcoreVersion}"))) { Console.WriteLine(" " + f); } diff --git a/test/TestLogger.Fixtures/TestAssetExtensions.cs b/test/TestLogger.Fixtures/TestAssetExtensions.cs index d2f6face..e7a9e496 100644 --- a/test/TestLogger.Fixtures/TestAssetExtensions.cs +++ b/test/TestLogger.Fixtures/TestAssetExtensions.cs @@ -30,15 +30,18 @@ public static string ToAssetDirectoryPath(this string assetName) /// Gets the full path to test asset assembly file. /// /// Name of the test asset. + /// Target framework moniker, e.g. net8.0. + /// Whether the MTP-flavored (rather than VSTest-flavored) output is wanted. /// Full path to test assembly. - public static string ToAssetAssemblyPath(this string assetName, string targetFrameworkVersion) + public static string ToAssetAssemblyPath(this string assetName, string targetFrameworkVersion, bool isMTP) { #if DEBUG var config = "Debug"; #else var config = "Release"; #endif - return Path.Combine(assetName.ToAssetDirectoryPath(), "bin", config, targetFrameworkVersion, $"{assetName}.dll"); + var flavor = isMTP ? "mtp" : "vstest"; + return Path.Combine(assetName.ToAssetDirectoryPath(), "bin", config, flavor, targetFrameworkVersion, $"{assetName}.dll"); } public static bool IsMTP(this TestContext context, Type type) diff --git a/test/TestLogger.UnitTests/InputSanitizerXmlTests.cs b/test/TestLogger.Mtp.UnitTests/InputSanitizerXmlTests.cs similarity index 100% rename from test/TestLogger.UnitTests/InputSanitizerXmlTests.cs rename to test/TestLogger.Mtp.UnitTests/InputSanitizerXmlTests.cs diff --git a/test/TestLogger.UnitTests/LegacyTestCaseNameParserTests.cs b/test/TestLogger.Mtp.UnitTests/LegacyTestCaseNameParserTests.cs similarity index 100% rename from test/TestLogger.UnitTests/LegacyTestCaseNameParserTests.cs rename to test/TestLogger.Mtp.UnitTests/LegacyTestCaseNameParserTests.cs diff --git a/test/TestLogger.UnitTests/Platform/BaseFileSystemTests.cs b/test/TestLogger.Mtp.UnitTests/Platform/BaseFileSystemTests.cs similarity index 100% rename from test/TestLogger.UnitTests/Platform/BaseFileSystemTests.cs rename to test/TestLogger.Mtp.UnitTests/Platform/BaseFileSystemTests.cs diff --git a/test/TestLogger.UnitTests/Platform/ConsoleOutputTests.cs b/test/TestLogger.Mtp.UnitTests/Platform/ConsoleOutputTests.cs similarity index 100% rename from test/TestLogger.UnitTests/Platform/ConsoleOutputTests.cs rename to test/TestLogger.Mtp.UnitTests/Platform/ConsoleOutputTests.cs diff --git a/test/TestLogger.UnitTests/Platform/FakeFileSystemTests.cs b/test/TestLogger.Mtp.UnitTests/Platform/FakeFileSystemTests.cs similarity index 100% rename from test/TestLogger.UnitTests/Platform/FakeFileSystemTests.cs rename to test/TestLogger.Mtp.UnitTests/Platform/FakeFileSystemTests.cs diff --git a/test/TestLogger.UnitTests/Platform/FileSystemTests.cs b/test/TestLogger.Mtp.UnitTests/Platform/FileSystemTests.cs similarity index 100% rename from test/TestLogger.UnitTests/Platform/FileSystemTests.cs rename to test/TestLogger.Mtp.UnitTests/Platform/FileSystemTests.cs diff --git a/test/TestLogger.UnitTests/TestAttachmentInfoTests.cs b/test/TestLogger.Mtp.UnitTests/TestAttachmentInfoTests.cs similarity index 100% rename from test/TestLogger.UnitTests/TestAttachmentInfoTests.cs rename to test/TestLogger.Mtp.UnitTests/TestAttachmentInfoTests.cs diff --git a/test/TestLogger.UnitTests/TestCaseNameParserTests.cs b/test/TestLogger.Mtp.UnitTests/TestCaseNameParserTests.cs similarity index 99% rename from test/TestLogger.UnitTests/TestCaseNameParserTests.cs rename to test/TestLogger.Mtp.UnitTests/TestCaseNameParserTests.cs index 605e3b25..8ade4d80 100644 --- a/test/TestLogger.UnitTests/TestCaseNameParserTests.cs +++ b/test/TestLogger.Mtp.UnitTests/TestCaseNameParserTests.cs @@ -11,6 +11,7 @@ namespace Spekt.TestLogger.UnitTests using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Spekt.TestLogger.Core; + using Spekt.TestLogger.Mtp.Core; using Spekt.TestLogger.UnitTests.TestDoubles; [TestClass] diff --git a/test/TestLogger.UnitTests/TestDoubles/FakeConsoleOutput.cs b/test/TestLogger.Mtp.UnitTests/TestDoubles/FakeConsoleOutput.cs similarity index 100% rename from test/TestLogger.UnitTests/TestDoubles/FakeConsoleOutput.cs rename to test/TestLogger.Mtp.UnitTests/TestDoubles/FakeConsoleOutput.cs diff --git a/test/TestLogger.UnitTests/TestDoubles/FakeFileSystem.cs b/test/TestLogger.Mtp.UnitTests/TestDoubles/FakeFileSystem.cs similarity index 100% rename from test/TestLogger.UnitTests/TestDoubles/FakeFileSystem.cs rename to test/TestLogger.Mtp.UnitTests/TestDoubles/FakeFileSystem.cs diff --git a/test/TestLogger.UnitTests/TestDoubles/InputSanitizerJson.cs b/test/TestLogger.Mtp.UnitTests/TestDoubles/InputSanitizerJson.cs similarity index 100% rename from test/TestLogger.UnitTests/TestDoubles/InputSanitizerJson.cs rename to test/TestLogger.Mtp.UnitTests/TestDoubles/InputSanitizerJson.cs diff --git a/test/TestLogger.UnitTests/TestDoubles/JsonTestResultSerializer.cs b/test/TestLogger.Mtp.UnitTests/TestDoubles/JsonTestResultSerializer.cs similarity index 100% rename from test/TestLogger.UnitTests/TestDoubles/JsonTestResultSerializer.cs rename to test/TestLogger.Mtp.UnitTests/TestDoubles/JsonTestResultSerializer.cs diff --git a/test/TestLogger.UnitTests/TestDoubles/MockCommandLineOptions.cs b/test/TestLogger.Mtp.UnitTests/TestDoubles/MockCommandLineOptions.cs similarity index 100% rename from test/TestLogger.UnitTests/TestDoubles/MockCommandLineOptions.cs rename to test/TestLogger.Mtp.UnitTests/TestDoubles/MockCommandLineOptions.cs diff --git a/test/TestLogger.UnitTests/TestDoubles/MockExtensions.cs b/test/TestLogger.Mtp.UnitTests/TestDoubles/MockExtensions.cs similarity index 100% rename from test/TestLogger.UnitTests/TestDoubles/MockExtensions.cs rename to test/TestLogger.Mtp.UnitTests/TestDoubles/MockExtensions.cs diff --git a/test/TestLogger.UnitTests/TestDoubles/TestableTestReporter.cs b/test/TestLogger.Mtp.UnitTests/TestDoubles/TestableTestReporter.cs similarity index 93% rename from test/TestLogger.UnitTests/TestDoubles/TestableTestReporter.cs rename to test/TestLogger.Mtp.UnitTests/TestDoubles/TestableTestReporter.cs index fa4bba95..d7ac4437 100644 --- a/test/TestLogger.UnitTests/TestDoubles/TestableTestReporter.cs +++ b/test/TestLogger.Mtp.UnitTests/TestDoubles/TestableTestReporter.cs @@ -11,7 +11,7 @@ namespace Spekt.TestLogger.UnitTests.TestDoubles /// /// Testable implementation of TestReporter for unit testing. /// - public class TestableTestReporter : Spekt.TestReporter.TestReporter + public class TestableTestReporter : Spekt.TestLogger.Mtp.TestReporter { public TestableTestReporter(IServiceProvider serviceProvider, IExtension extension) : base(serviceProvider, extension, "junit") diff --git a/test/TestLogger.Mtp.UnitTests/TestLogger.Mtp.UnitTests.csproj b/test/TestLogger.Mtp.UnitTests/TestLogger.Mtp.UnitTests.csproj new file mode 100644 index 00000000..75bc172c --- /dev/null +++ b/test/TestLogger.Mtp.UnitTests/TestLogger.Mtp.UnitTests.csproj @@ -0,0 +1,36 @@ + + + + net10.0 + true + true + false + true + Exe + true + true + + + + Spekt.TestLogger.UnitTests + + + + + + + + + + + + + + + + + + + + + diff --git a/test/TestLogger.UnitTests/TestReporterCommandLineProviderTests.cs b/test/TestLogger.Mtp.UnitTests/TestReporterCommandLineProviderTests.cs similarity index 99% rename from test/TestLogger.UnitTests/TestReporterCommandLineProviderTests.cs rename to test/TestLogger.Mtp.UnitTests/TestReporterCommandLineProviderTests.cs index 5aabcde0..49725f3c 100644 --- a/test/TestLogger.UnitTests/TestReporterCommandLineProviderTests.cs +++ b/test/TestLogger.Mtp.UnitTests/TestReporterCommandLineProviderTests.cs @@ -11,7 +11,7 @@ namespace Spekt.TestLogger.UnitTests using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.CommandLine; using Microsoft.VisualStudio.TestTools.UnitTesting; - using Spekt.TestReporter; + using Spekt.TestLogger.Mtp; [TestClass] public class TestReporterCommandLineProviderTests diff --git a/test/TestLogger.UnitTests/TestReporterTests.cs b/test/TestLogger.Mtp.UnitTests/TestReporterTests.cs similarity index 99% rename from test/TestLogger.UnitTests/TestReporterTests.cs rename to test/TestLogger.Mtp.UnitTests/TestReporterTests.cs index db2a747e..08f33195 100644 --- a/test/TestLogger.UnitTests/TestReporterTests.cs +++ b/test/TestLogger.Mtp.UnitTests/TestReporterTests.cs @@ -9,8 +9,8 @@ namespace Spekt.TestLogger.UnitTests using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Spekt.TestLogger.Core; + using Spekt.TestLogger.Mtp; using Spekt.TestLogger.UnitTests.TestDoubles; - using Spekt.TestReporter; [TestClass] public class TestReporterTests diff --git a/test/TestLogger.UnitTests/Utilities/ArtifactExtensionsTests.cs b/test/TestLogger.Mtp.UnitTests/Utilities/ArtifactExtensionsTests.cs similarity index 99% rename from test/TestLogger.UnitTests/Utilities/ArtifactExtensionsTests.cs rename to test/TestLogger.Mtp.UnitTests/Utilities/ArtifactExtensionsTests.cs index 3a0096ab..62b165be 100644 --- a/test/TestLogger.UnitTests/Utilities/ArtifactExtensionsTests.cs +++ b/test/TestLogger.Mtp.UnitTests/Utilities/ArtifactExtensionsTests.cs @@ -9,6 +9,7 @@ namespace Spekt.TestLogger.UnitTests.Utilities using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.TestHost; using Microsoft.VisualStudio.TestTools.UnitTesting; + using Spekt.TestLogger.Mtp.Utilities; using Spekt.TestLogger.Utilities; [TestClass] diff --git a/test/TestLogger.UnitTests/Utilities/StringExtensionsTests.cs b/test/TestLogger.Mtp.UnitTests/Utilities/StringExtensionsTests.cs similarity index 100% rename from test/TestLogger.UnitTests/Utilities/StringExtensionsTests.cs rename to test/TestLogger.Mtp.UnitTests/Utilities/StringExtensionsTests.cs diff --git a/test/TestLogger.PackageTests/TestLogger.PackageTests.csproj b/test/TestLogger.PackageTests/TestLogger.PackageTests.csproj index b09c6416..6821a109 100644 --- a/test/TestLogger.PackageTests/TestLogger.PackageTests.csproj +++ b/test/TestLogger.PackageTests/TestLogger.PackageTests.csproj @@ -37,7 +37,7 @@ - + diff --git a/test/TestLogger.PackageTests/TestLoggerPackageTests.cs b/test/TestLogger.PackageTests/TestLoggerPackageTests.cs index e05187f4..3c95fb2a 100644 --- a/test/TestLogger.PackageTests/TestLoggerPackageTests.cs +++ b/test/TestLogger.PackageTests/TestLoggerPackageTests.cs @@ -28,7 +28,7 @@ public void VerifyTestRunOutput(string testAssembly, string loggerName, string c // Collect coverage will attach a runlevel attachment. var collectCoverage = testAssembly.Contains("XUnit.NetCore"); - var resultsFile = DotnetTestFixture.Create().WithBuild().Execute(testAssembly, loggerArgs, collectCoverage, "test-results.xml"); + var resultsFile = DotnetTestFixture.Create().Execute(testAssembly, loggerArgs, collectCoverage, "test-results.xml"); var testReport = File.ReadAllText(resultsFile); Assert.IsNotNull(testReport); diff --git a/test/TestLogger.UnitTests/TestLogger.UnitTests.csproj b/test/TestLogger.UnitTests/TestLogger.UnitTests.csproj deleted file mode 100644 index 838bb1b3..00000000 --- a/test/TestLogger.UnitTests/TestLogger.UnitTests.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - net10.0 - true - true - false - true - - - - Spekt.TestLogger.UnitTests - - - - - - - - - - - - - runtime; build; native; contentfiles; analyzers - all - - - - - diff --git a/test/TestLogger.UnitTests/Builders/TestResultInfoBuilder.cs b/test/TestLogger.VSTest.UnitTests/Builders/TestResultInfoBuilder.cs similarity index 91% rename from test/TestLogger.UnitTests/Builders/TestResultInfoBuilder.cs rename to test/TestLogger.VSTest.UnitTests/Builders/TestResultInfoBuilder.cs index 8e2f278f..cb8e1a70 100644 --- a/test/TestLogger.UnitTests/Builders/TestResultInfoBuilder.cs +++ b/test/TestLogger.VSTest.UnitTests/Builders/TestResultInfoBuilder.cs @@ -7,6 +7,8 @@ namespace Spekt.TestLogger.UnitTests.Builders using System.Collections.Generic; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Spekt.TestLogger.Core; + using TestOutcome = Spekt.TestLogger.Core.TestOutcome; + using Trait = Spekt.TestLogger.Core.Trait; internal class TestResultInfoBuilder { @@ -34,6 +36,8 @@ internal TestResultInfoBuilder( this.method = method; } + internal TestCase TestCase => this.testCase; + internal TestResultInfoBuilder WithOutcome(TestOutcome outcome) { this.outcome = outcome; @@ -73,7 +77,7 @@ internal TestResultInfoBuilder WithAttachment(TestAttachmentInfo attachment) internal TestResultInfo Build() { - return new TestResultInfo( + var result = new TestResultInfo( this.@namespace, this.type, this.method, @@ -92,8 +96,9 @@ internal TestResultInfo Build() new(), this.attachments, this.traits, - "executor://dummy", - this.testCase); + "executor://dummy"); + + return result; } } } diff --git a/test/TestLogger.UnitTests/Extensions/MSTestAdapterTests.cs b/test/TestLogger.VSTest.UnitTests/Extensions/MSTestAdapterTests.cs similarity index 87% rename from test/TestLogger.UnitTests/Extensions/MSTestAdapterTests.cs rename to test/TestLogger.VSTest.UnitTests/Extensions/MSTestAdapterTests.cs index 41607865..5780741b 100644 --- a/test/TestLogger.UnitTests/Extensions/MSTestAdapterTests.cs +++ b/test/TestLogger.VSTest.UnitTests/Extensions/MSTestAdapterTests.cs @@ -10,8 +10,8 @@ namespace Spekt.TestLogger.UnitTests.Extensions using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using Spekt.TestLogger.Core; - using Spekt.TestLogger.Extensions; using Spekt.TestLogger.UnitTests.Builders; + using Spekt.TestLogger.VSTest.Extensions; using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult; [TestClass] @@ -59,16 +59,14 @@ public void TransformShouldNoitOverwriteMethodEmptyValues(string displayName) [TestMethod] public void TransformResultShouldAddProperties() { - var testResults = new List - { - new TestResultInfoBuilder("namespace", "type", Method) - .WithProperty("Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestProperty", new[] { "c1", "c2" }) - .Build(), - }; + var builder = new TestResultInfoBuilder("namespace", "type", Method) + .WithProperty("Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestProperty", new[] { "c1", "c2" }); + + var testResults = new List { builder.Build() }; var sut = new MSTestAdapter(); - var transformedResults = sut.TransformResults(testResults, new List()); + var transformedResults = sut.TransformResults(testResults, new List(), builder.TestCase); Assert.AreEqual(1, transformedResults.Count); Assert.AreEqual(1, transformedResults[0].Properties.Count); diff --git a/test/TestLogger.UnitTests/Extensions/NUnitTestAdapterTests.cs b/test/TestLogger.VSTest.UnitTests/Extensions/NUnitTestAdapterTests.cs similarity index 81% rename from test/TestLogger.UnitTests/Extensions/NUnitTestAdapterTests.cs rename to test/TestLogger.VSTest.UnitTests/Extensions/NUnitTestAdapterTests.cs index 6c1b2f5f..603472a7 100644 --- a/test/TestLogger.UnitTests/Extensions/NUnitTestAdapterTests.cs +++ b/test/TestLogger.VSTest.UnitTests/Extensions/NUnitTestAdapterTests.cs @@ -8,8 +8,10 @@ namespace Spekt.TestLogger.UnitTests.Extensions using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; using Spekt.TestLogger.Core; - using Spekt.TestLogger.Extensions; using Spekt.TestLogger.UnitTests.Builders; + using Spekt.TestLogger.VSTest.Extensions; + using TestOutcome = Spekt.TestLogger.Core.TestOutcome; + using Trait = Spekt.TestLogger.Core.Trait; [TestClass] public class NUnitTestAdapterTests @@ -80,17 +82,15 @@ public void TransformResultShouldModifyTestWithExplicitAttributeAndNoOutcome() [TestMethod] public void TransformResultShouldAddPropertiesIfAvailable() { - var results = new List - { - new TestResultInfoBuilder(DummyNamespace, DummyType, DummyMethod) - .WithOutcome(TestOutcome.Passed) - .WithProperty("NUnit.Seed", 1) - .WithProperty("NUnit.TestCategory", new[] { "c1", "c2" }) - .WithProperty("NUnit.Unsupported", true) - .Build() - }; + var builder = new TestResultInfoBuilder(DummyNamespace, DummyType, DummyMethod) + .WithOutcome(TestOutcome.Passed) + .WithProperty("NUnit.Seed", 1) + .WithProperty("NUnit.TestCategory", new[] { "c1", "c2" }) + .WithProperty("NUnit.Unsupported", true); - var modifiedResults = this.adapter.TransformResults(results, new()); + var results = new List { builder.Build() }; + + var modifiedResults = this.adapter.TransformResults(results, new(), builder.TestCase); Assert.AreEqual(1, modifiedResults.Count); Assert.AreEqual(2, modifiedResults[0].Properties.Count); @@ -101,15 +101,13 @@ public void TransformResultShouldAddPropertiesIfAvailable() [TestMethod] public void TransformResultShouldAddProperties() { - var results = new List - { - new TestResultInfoBuilder(DummyNamespace, DummyType, DummyMethod) - .WithOutcome(TestOutcome.Passed) - .WithProperty("NUnit.Category", new[] { "c1", "c2" }) - .Build() - }; + var builder = new TestResultInfoBuilder(DummyNamespace, DummyType, DummyMethod) + .WithOutcome(TestOutcome.Passed) + .WithProperty("NUnit.Category", new[] { "c1", "c2" }); - var modifiedResults = this.adapter.TransformResults(results, new()); + var results = new List { builder.Build() }; + + var modifiedResults = this.adapter.TransformResults(results, new(), builder.TestCase); Assert.AreEqual(1, modifiedResults.Count); Assert.AreEqual(1, modifiedResults[0].Properties.Count); diff --git a/test/TestLogger.UnitTests/Extensions/TestAdapterFactoryTests.cs b/test/TestLogger.VSTest.UnitTests/Extensions/TestAdapterFactoryTests.cs similarity index 97% rename from test/TestLogger.UnitTests/Extensions/TestAdapterFactoryTests.cs rename to test/TestLogger.VSTest.UnitTests/Extensions/TestAdapterFactoryTests.cs index f707193a..069962a9 100644 --- a/test/TestLogger.UnitTests/Extensions/TestAdapterFactoryTests.cs +++ b/test/TestLogger.VSTest.UnitTests/Extensions/TestAdapterFactoryTests.cs @@ -4,7 +4,7 @@ namespace Spekt.TestLogger.UnitTests.Extensions { using Microsoft.VisualStudio.TestTools.UnitTesting; - using Spekt.TestLogger.Extensions; + using Spekt.TestLogger.VSTest.Extensions; [TestClass] public class TestAdapterFactoryTests diff --git a/test/TestLogger.UnitTests/Extensions/XunitTestAdapterTests.cs b/test/TestLogger.VSTest.UnitTests/Extensions/XunitTestAdapterTests.cs similarity index 56% rename from test/TestLogger.UnitTests/Extensions/XunitTestAdapterTests.cs rename to test/TestLogger.VSTest.UnitTests/Extensions/XunitTestAdapterTests.cs index a87a6a3e..4101c379 100644 --- a/test/TestLogger.UnitTests/Extensions/XunitTestAdapterTests.cs +++ b/test/TestLogger.VSTest.UnitTests/Extensions/XunitTestAdapterTests.cs @@ -10,9 +10,13 @@ namespace Spekt.TestLogger.UnitTests.Extensions using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using Spekt.TestLogger.Core; - using Spekt.TestLogger.Extensions; using Spekt.TestLogger.UnitTests.Builders; + using Spekt.TestLogger.VSTest.Extensions; + using TestMessageLevel = Spekt.TestLogger.Core.TestMessageLevel; + using TestOutcome = Spekt.TestLogger.Core.TestOutcome; using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult; + using TestResultMessage = Spekt.TestLogger.Core.TestResultMessage; + using Trait = Spekt.TestLogger.Core.Trait; [TestClass] public class XunitTestAdapterTests @@ -40,6 +44,45 @@ public void TransformShouldAddReasonForSkippedTests() Assert.AreEqual("Dummy reason", transformedResults[1].Messages[0].Text); } + [TestMethod] + public void TransformShouldAddReasonFromResultMessageForSkippedTests() + { + var result = new TestResultInfoBuilder("N", "C", "M1") + .WithOutcome(TestOutcome.Skipped) + .WithDisplayName("N.C.M1") + .Build(); + result.Messages.Add(new TestResultMessage(TestResultMessage.StandardOutCategory, "Skipped")); + var xunit = new XunitTestAdapter(); + + var transformedResults = xunit.TransformResults(new List { result }, new List()); + + var reasons = transformedResults[0].Messages.Where(m => m.Category == "skipReason").ToList(); + Assert.AreEqual(1, reasons.Count); + Assert.AreEqual("Skipped", reasons[0].Text); + } + + [TestMethod] + public void TransformShouldNotDuplicateReasonWhenBothSourcesMatch() + { + var result = new TestResultInfoBuilder("N", "C", "M2") + .WithOutcome(TestOutcome.Skipped) + .WithDisplayName("N.C.M2") + .Build(); + result.Messages.Add(new TestResultMessage(TestResultMessage.StandardOutCategory, "Skipped")); + var messages = new List + { + new(TestMessageLevel.Informational, "[xUnit.net 00:00:00.6490557] N.C.M2 [SKIP]"), + new(TestMessageLevel.Informational, "[SKIP] Dummy reason"), + }; + var xunit = new XunitTestAdapter(); + + var transformedResults = xunit.TransformResults(new List { result }, messages); + + var reasons = transformedResults[0].Messages.Where(m => m.Category == "skipReason").ToList(); + Assert.AreEqual(1, reasons.Count); + Assert.AreEqual("Dummy reason", reasons[0].Text); + } + [TestMethod] public void TransformShouldAddParameterData() { @@ -61,19 +104,17 @@ public void TransformShouldAddParameterData() [TestMethod] public void TransformResultShouldAddProperties() { - var results = new List - { - new TestResultInfoBuilder("N", "C", "M1") - .WithOutcome(TestOutcome.Passed) - .WithTraits([new Trait("traitKey", "traitVal")]) - .WithProperty("Xunit.Trait", new string[] { "key", "val" }) - .Build() - }; + var builder = new TestResultInfoBuilder("N", "C", "M1") + .WithOutcome(TestOutcome.Passed) + .WithTraits([new Trait("traitKey", "traitVal")]) + .WithProperty("Xunit.Trait", new string[] { "key", "val" }); + + var results = new List { builder.Build() }; var messages = new List(); var xunit = new XunitTestAdapter(); - var transformedResults = xunit.TransformResults(results, messages); + var transformedResults = xunit.TransformResults(results, messages, builder.TestCase); Assert.AreEqual(1, transformedResults.Count); Assert.AreEqual(1, transformedResults.Count(x => x.Method == "M1")); diff --git a/test/TestLogger.UnitTests/LoggerConfigurationTests.cs b/test/TestLogger.VSTest.UnitTests/LoggerConfigurationTests.cs similarity index 98% rename from test/TestLogger.UnitTests/LoggerConfigurationTests.cs rename to test/TestLogger.VSTest.UnitTests/LoggerConfigurationTests.cs index 6fdbeae7..7f1a6668 100644 --- a/test/TestLogger.UnitTests/LoggerConfigurationTests.cs +++ b/test/TestLogger.VSTest.UnitTests/LoggerConfigurationTests.cs @@ -8,6 +8,7 @@ namespace Spekt.TestLogger.UnitTests using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; using Spekt.TestLogger.Core; + using DefaultLoggerParameterNames = Spekt.TestLogger.Core.DefaultLoggerParameterNames; [TestClass] public class LoggerConfigurationTests diff --git a/test/TestLogger.VSTest.UnitTests/TestDoubles/FakeConsoleOutput.cs b/test/TestLogger.VSTest.UnitTests/TestDoubles/FakeConsoleOutput.cs new file mode 100644 index 00000000..229df10d --- /dev/null +++ b/test/TestLogger.VSTest.UnitTests/TestDoubles/FakeConsoleOutput.cs @@ -0,0 +1,27 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.UnitTests.TestDoubles +{ + using System.Collections.Generic; + using Spekt.TestLogger.Platform; + + public class FakeConsoleOutput : IConsoleOutput + { + public FakeConsoleOutput() => this.Messages = new List<(string, string)>(); + +#pragma warning disable SA1414 // Tuple types in signatures should have element names + public List<(string, string)> Messages { get; private set; } +#pragma warning restore SA1414 // Tuple types in signatures should have element names + + public void WriteMessage(string message) + { + this.Messages.Add(("stdout", message)); + } + + public void WriteError(string message) + { + this.Messages.Add(("stderr", message)); + } + } +} \ No newline at end of file diff --git a/test/TestLogger.VSTest.UnitTests/TestDoubles/FakeFileSystem.cs b/test/TestLogger.VSTest.UnitTests/TestDoubles/FakeFileSystem.cs new file mode 100644 index 00000000..eb7d34c2 --- /dev/null +++ b/test/TestLogger.VSTest.UnitTests/TestDoubles/FakeFileSystem.cs @@ -0,0 +1,65 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.UnitTests.TestDoubles +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using Spekt.TestLogger.Platform; + + public class FakeFileSystem : IFileSystem + { + private readonly Dictionary files; + private readonly HashSet directories; + + public FakeFileSystem() + { + this.files = new Dictionary(); + this.directories = new HashSet { Path.GetTempPath() }; + } + + public void CreateDirectory(string path) + { + this.directories.Add(path); + } + + public bool ExistsDirectory(string path) + { + return this.directories.Contains(path); + } + + public void RemoveDirectory(string path) + { + // Remove all paths which could be children of provided path + foreach (var p in this.directories.Where(p => p.StartsWith(path)).ToList()) + { + this.directories.Remove(p); + } + } + + public string Read(string path) + { + if (this.files.TryGetValue(path, out var content)) + { + return content; + } + + throw new ArgumentException("File does not exist.", nameof(path)); + } + + public void Write(string path, string content) + { + this.files[path] = content; + } + + public void Delete(string path) + { + if (this.files.ContainsKey(path)) + { + this.files.Remove(path); + } + } + } +} \ No newline at end of file diff --git a/test/TestLogger.VSTest.UnitTests/TestDoubles/InputSanitizerJson.cs b/test/TestLogger.VSTest.UnitTests/TestDoubles/InputSanitizerJson.cs new file mode 100644 index 00000000..fbb46f86 --- /dev/null +++ b/test/TestLogger.VSTest.UnitTests/TestDoubles/InputSanitizerJson.cs @@ -0,0 +1,79 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.UnitTests.TestDoubles +{ + using System.Text; + using Spekt.TestLogger.Core; + + /// + /// Sanitizes JSON input. NOTE, this is not optimized code, so is currently appropriate for test use only. + /// + public class InputSanitizerJson : IInputSanitizer + { + private static readonly char[] EscapeTable; + private static readonly char[] EscapeCharacters = { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; + + static InputSanitizerJson() + { + EscapeTable = new char[93]; + EscapeTable['"'] = '"'; + EscapeTable['\\'] = '\\'; + EscapeTable['\b'] = 'b'; + EscapeTable['\f'] = 'f'; + EscapeTable['\n'] = 'n'; + EscapeTable['\r'] = 'r'; + EscapeTable['\t'] = 't'; + } + + public string Sanitize(string input) + { + var sb = new StringBuilder(); + + if (input is null) + { + return null; + } + + // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) + if (input.IndexOfAny(EscapeCharacters) == -1) + { + return input; + } + + int safeCharacterCount = 0; + char[] charArray = input.ToCharArray(); + + for (int i = 0; i < charArray.Length; i++) + { + char c = charArray[i]; + + // Non ascii characters are fine, buffer them up and send them to the builder + // in larger chunks if possible. The escape table is a 1:1 translation table + // with \0 [default(char)] denoting a safe character. + if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) + { + safeCharacterCount++; + } + else + { + if (safeCharacterCount > 0) + { + sb.Append(charArray, i - safeCharacterCount, safeCharacterCount); + safeCharacterCount = 0; + } + + sb.Append('\\'); + sb.Append((char)EscapeTable[c]); + } + } + + if (safeCharacterCount > 0) + { + sb.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); + } + + return sb.ToString(); + } + } +} diff --git a/test/TestLogger.VSTest.UnitTests/TestDoubles/JsonTestResultSerializer.cs b/test/TestLogger.VSTest.UnitTests/TestDoubles/JsonTestResultSerializer.cs new file mode 100644 index 00000000..a2877ae8 --- /dev/null +++ b/test/TestLogger.VSTest.UnitTests/TestDoubles/JsonTestResultSerializer.cs @@ -0,0 +1,170 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Spekt.TestLogger.UnitTests.TestDoubles +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using Newtonsoft.Json; + using Spekt.TestLogger.Core; + + /// + /// Test logger to serialize results into json format. + /// + /// + /// Sample json output: + /// + /// { + /// "TestAssemblies": [ + /// { + /// "Name": "TestAssembly", + /// "Fixtures": [ + /// { + /// "Name": "TestClass", + /// "Tests": [ + /// { + /// "Name": "TestMethod", + /// "Result": (pass|fail|skipped), + /// } + /// ] + /// } + /// ] + /// } + /// ], + /// "TestMessages": [ + /// { + /// "Level": (0,1,2), + /// "Message": "MessageText" + /// } + /// ] + /// } + /// + /// + public class JsonTestResultSerializer : ITestResultSerializer + { + public IInputSanitizer InputSanitizer { get; } = new InputSanitizerJson(); + + public string Serialize( + LoggerConfiguration loggerConfiguration, + TestRunConfiguration runConfiguration, + List results, + List messages) + { + var res = from r in results + group r by r.AssemblyPath + into assemblies + orderby assemblies.Key + select this.CreateAssembly(assemblies); + + var content = new StringBuilder(); + new JsonSerializer().Serialize(new StringWriter(content), new TestReport(res, messages, runConfiguration.Attachments)); + return content.ToString(); + } + + private TestAssembly CreateAssembly( + IGrouping resultsByAssembly) + { + return new() + { + Name = resultsByAssembly.Key, + Fixtures = resultsByAssembly.GroupBy(a => a.Type).Select(this.CreateFixture) + }; + } + + private TestFixture CreateFixture( + IGrouping resultsByType) + { + return new() + { + Name = resultsByType.Key, + Tests = resultsByType.Select(this.CreateTest) + }; + } + + private Test CreateTest(TestResultInfo result) + { + // Mangle output to ensure predictable report + var props = result + .Properties + .Select(p => p.Key == "NUnit.Seed" ? new KeyValuePair(p.Key, "1100") : p) + .OrderBy(p => p.Key) + .ToList(); + + // Attachments have diff path in Windows vs Linux. + // MSTest duplicates the path in description. + var attachments = result.Attachments + .Select(a => new TestAttachmentInfo(Path.GetFileName(a.FilePath), "dummyDescription")) + .ToList(); + return new() + { + FullyQualifiedName = result.FullyQualifiedName, + DisplayName = result.DisplayName, + Namespace = result.Namespace, + Type = result.Type, + Method = result.Method, + Result = result.Outcome.ToString(), + Traits = result.Traits.Select(t => new KeyValuePair(t.Name, t.Value)).ToList(), + Properties = props, + Attachments = attachments + }; + } + + public class TestReport + { + public TestReport(IEnumerable testAssemblies, IEnumerable testMessages, IReadOnlyCollection attachments) + { + this.TestAssemblies = testAssemblies ?? throw new ArgumentNullException(nameof(testAssemblies)); + this.TestMessages = testMessages ?? throw new ArgumentNullException(nameof(testMessages)); + + // Mangle attachments to predictable filepath + this.Attachments = attachments + .Select(a => new TestAttachmentInfo(Path.GetFileName(a.FilePath), a.Description)) + .ToList(); + } + + public IEnumerable TestAssemblies { get; set; } + + public IEnumerable TestMessages { get; set; } + + public IReadOnlyCollection Attachments { get; set; } + } + + public class TestAssembly + { + public string Name { get; set; } + + public IEnumerable Fixtures { get; set; } + } + + public class TestFixture + { + public string Name { get; set; } + + public IEnumerable Tests { get; set; } + } + + public class Test + { + public string FullyQualifiedName { get; set; } + + public string DisplayName { get; set; } + + public string Namespace { get; set; } + + public string Type { get; set; } + + public string Method { get; set; } + + public string Result { get; set; } + + public List> Traits { get; set; } + + public List> Properties { get; set; } + + public List Attachments { get; set; } + } + } +} diff --git a/test/TestLogger.UnitTests/TestDoubles/MockTestLoggerEvents.cs b/test/TestLogger.VSTest.UnitTests/TestDoubles/MockTestLoggerEvents.cs similarity index 100% rename from test/TestLogger.UnitTests/TestDoubles/MockTestLoggerEvents.cs rename to test/TestLogger.VSTest.UnitTests/TestDoubles/MockTestLoggerEvents.cs diff --git a/test/TestLogger.UnitTests/TestDoubles/TestableTestLogger.cs b/test/TestLogger.VSTest.UnitTests/TestDoubles/TestableTestLogger.cs similarity index 94% rename from test/TestLogger.UnitTests/TestDoubles/TestableTestLogger.cs rename to test/TestLogger.VSTest.UnitTests/TestDoubles/TestableTestLogger.cs index 977e53c8..b244aec1 100644 --- a/test/TestLogger.UnitTests/TestDoubles/TestableTestLogger.cs +++ b/test/TestLogger.VSTest.UnitTests/TestDoubles/TestableTestLogger.cs @@ -4,6 +4,7 @@ namespace Spekt.TestLogger.UnitTests.TestDoubles { using Spekt.TestLogger.Core; + using Spekt.TestLogger.VSTest; public class TestableTestLogger : TestLogger { diff --git a/test/TestLogger.VSTest.UnitTests/TestLogger.VSTest.UnitTests.csproj b/test/TestLogger.VSTest.UnitTests/TestLogger.VSTest.UnitTests.csproj new file mode 100644 index 00000000..1ca72bed --- /dev/null +++ b/test/TestLogger.VSTest.UnitTests/TestLogger.VSTest.UnitTests.csproj @@ -0,0 +1,35 @@ + + + + net10.0 + true + true + false + true + Exe + true + true + + + + Spekt.TestLogger.UnitTests + + + + + + + + + + + + + + + + + + + + diff --git a/test/TestLogger.UnitTests/TestLoggerTests.cs b/test/TestLogger.VSTest.UnitTests/TestLoggerTests.cs similarity index 98% rename from test/TestLogger.UnitTests/TestLoggerTests.cs rename to test/TestLogger.VSTest.UnitTests/TestLoggerTests.cs index 110b05fe..d429d9d9 100644 --- a/test/TestLogger.UnitTests/TestLoggerTests.cs +++ b/test/TestLogger.VSTest.UnitTests/TestLoggerTests.cs @@ -8,9 +8,10 @@ namespace Spekt.TestLogger.UnitTests using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; - using Spekt.TestLogger; using Spekt.TestLogger.Core; using Spekt.TestLogger.UnitTests.TestDoubles; + using Spekt.TestLogger.VSTest; + using DefaultLoggerParameterNames = Spekt.TestLogger.Core.DefaultLoggerParameterNames; [TestClass] public class TestLoggerTests diff --git a/test/TestLogger.UnitTests/TestResultInfoTests.cs b/test/TestLogger.VSTest.UnitTests/TestResultInfoTests.cs similarity index 100% rename from test/TestLogger.UnitTests/TestResultInfoTests.cs rename to test/TestLogger.VSTest.UnitTests/TestResultInfoTests.cs diff --git a/test/TestLogger.UnitTests/TestRunBuilderTests.cs b/test/TestLogger.VSTest.UnitTests/TestRunBuilderTests.cs similarity index 89% rename from test/TestLogger.UnitTests/TestRunBuilderTests.cs rename to test/TestLogger.VSTest.UnitTests/TestRunBuilderTests.cs index bd911d60..7c1ea02d 100644 --- a/test/TestLogger.UnitTests/TestRunBuilderTests.cs +++ b/test/TestLogger.VSTest.UnitTests/TestRunBuilderTests.cs @@ -12,6 +12,9 @@ namespace Spekt.TestLogger.UnitTests using Microsoft.VisualStudio.TestTools.UnitTesting; using Spekt.TestLogger.Core; using Spekt.TestLogger.UnitTests.TestDoubles; + using Spekt.TestLogger.VSTest.Core; + using DefaultLoggerParameterNames = Spekt.TestLogger.Core.DefaultLoggerParameterNames; + using TestMessageLevel = Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging.TestMessageLevel; [TestClass] public class TestRunBuilderTests @@ -31,14 +34,6 @@ public void TestRunBuilderShouldCreateDefaultRunConfiguration() Assert.IsNotNull(run.RunConfiguration); } - [TestMethod] - public void TestRunBuilderShouldCreateDefaultTestAdapterFactory() - { - var run = this.testRunBuilder.Build(); - - Assert.IsNotNull(run.AdapterFactory); - } - [TestMethod] public void WithLoggerConfigurationShouldSetTestLoggerConfiguration() { @@ -68,7 +63,8 @@ public void WithSerializerShouldThrowForNullTestResultSerializer() [TestMethod] public void SubscribeShouldThrowForNullLoggerEvents() { - Assert.ThrowsExactly(() => this.testRunBuilder.Subscribe(null)); + var run = this.testRunBuilder.Build(); + Assert.ThrowsExactly(() => run.Subscribe(null)); } [TestMethod] @@ -77,7 +73,8 @@ public void SubscribeShouldSetupTraceAndThrowExceptionForEvents() var testEvents = new MockTestLoggerEvents(); var consoleOutput = new FakeConsoleOutput(); - this.testRunBuilder.WithConsoleOutput(consoleOutput).Subscribe(testEvents); + var run = this.testRunBuilder.WithConsoleOutput(consoleOutput).Build(); + run.Subscribe(testEvents); Assert.ThrowsExactly(() => testEvents.RaiseTestRunMessage(TestMessageLevel.Error, "dummy message")); Assert.ThrowsExactly(() => testEvents.RaiseTestResult(new Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult(new TestCase()))); diff --git a/test/TestLogger.UnitTests/TestRunCompleteWorkflowTests.cs b/test/TestLogger.VSTest.UnitTests/TestRunCompleteWorkflowTests.cs similarity index 96% rename from test/TestLogger.UnitTests/TestRunCompleteWorkflowTests.cs rename to test/TestLogger.VSTest.UnitTests/TestRunCompleteWorkflowTests.cs index 18afa339..2c1b6e78 100644 --- a/test/TestLogger.UnitTests/TestRunCompleteWorkflowTests.cs +++ b/test/TestLogger.VSTest.UnitTests/TestRunCompleteWorkflowTests.cs @@ -12,7 +12,10 @@ namespace Spekt.TestLogger.UnitTests using Microsoft.VisualStudio.TestTools.UnitTesting; using Spekt.TestLogger.Core; using Spekt.TestLogger.UnitTests.TestDoubles; + using Spekt.TestLogger.VSTest.Core; using JsonSerializer = System.Text.Json.JsonSerializer; + using TestMessageLevel = Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging.TestMessageLevel; + using TestOutcome = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome; using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult; [TestClass] @@ -128,7 +131,7 @@ public void CompleteShouldPassAllMessagesToSerializer() Assert.AreEqual(expectedMessages.Count, results.TestMessages.Count()); expectedMessages .ForEach(exp => Assert.IsTrue( - results.TestMessages.SingleOrDefault(act => act.Level == exp.Level && act.Message == exp.Message) is TestMessageInfo)); + results.TestMessages.SingleOrDefault(act => (int)act.Level == (int)exp.Level && act.Message == exp.Message) is TestMessageInfo)); } [TestMethod] diff --git a/test/TestLogger.UnitTests/TestRunMessageWorkflowTests.cs b/test/TestLogger.VSTest.UnitTests/TestRunMessageWorkflowTests.cs similarity index 84% rename from test/TestLogger.UnitTests/TestRunMessageWorkflowTests.cs rename to test/TestLogger.VSTest.UnitTests/TestRunMessageWorkflowTests.cs index c217cbea..13568ecb 100644 --- a/test/TestLogger.UnitTests/TestRunMessageWorkflowTests.cs +++ b/test/TestLogger.VSTest.UnitTests/TestRunMessageWorkflowTests.cs @@ -7,6 +7,8 @@ namespace Spekt.TestLogger.UnitTests using Microsoft.VisualStudio.TestTools.UnitTesting; using Spekt.TestLogger.Core; using Spekt.TestLogger.UnitTests.TestDoubles; + using Spekt.TestLogger.VSTest.Core; + using TestMessageLevel = Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging.TestMessageLevel; [TestClass] public class TestRunMessageWorkflowTests @@ -32,7 +34,7 @@ public void MessageShouldStoreRunMessages() testRun.Store.Pop(out _, out var messages); Assert.AreEqual(1, messages.Count); - Assert.AreEqual(TestMessageLevel.Informational, messages[0].Level); + Assert.AreEqual((int)TestMessageLevel.Informational, (int)messages[0].Level); Assert.AreEqual("Dummy message", messages[0].Message); } } diff --git a/test/TestLogger.UnitTests/TestRunResultWorkflowTests.cs b/test/TestLogger.VSTest.UnitTests/TestRunResultWorkflowTests.cs similarity index 95% rename from test/TestLogger.UnitTests/TestRunResultWorkflowTests.cs rename to test/TestLogger.VSTest.UnitTests/TestRunResultWorkflowTests.cs index 971a2f56..34bd878f 100644 --- a/test/TestLogger.UnitTests/TestRunResultWorkflowTests.cs +++ b/test/TestLogger.VSTest.UnitTests/TestRunResultWorkflowTests.cs @@ -10,6 +10,9 @@ namespace Spekt.TestLogger.UnitTests using Microsoft.VisualStudio.TestTools.UnitTesting; using Spekt.TestLogger.Core; using Spekt.TestLogger.UnitTests.TestDoubles; + using Spekt.TestLogger.VSTest.Core; + using DefaultLoggerParameterNames = Spekt.TestLogger.Core.DefaultLoggerParameterNames; + using TestOutcome = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome; using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult; [TestClass] @@ -54,7 +57,7 @@ public void ResultShouldCaptureTestCaseAndResult(TestOutcome testOutcome) Assert.AreEqual("SampleNamespace.SampleClass.SampleTest", results[0].DisplayName); Assert.AreEqual(DummySourceFile, results[0].AssemblyPath); - Assert.AreEqual(testOutcome, results[0].Outcome); + Assert.AreEqual((int)testOutcome, (int)results[0].Outcome); Assert.AreEqual("Dummy error", results[0].ErrorMessage); Assert.AreEqual("Dummy stacktrace", results[0].ErrorStackTrace); diff --git a/test/TestLogger.UnitTests/TestRunSimulator.cs b/test/TestLogger.VSTest.UnitTests/TestRunSimulator.cs similarity index 98% rename from test/TestLogger.UnitTests/TestRunSimulator.cs rename to test/TestLogger.VSTest.UnitTests/TestRunSimulator.cs index c2af2b76..34564bdc 100644 --- a/test/TestLogger.UnitTests/TestRunSimulator.cs +++ b/test/TestLogger.VSTest.UnitTests/TestRunSimulator.cs @@ -11,8 +11,8 @@ namespace Spekt.TestLogger.UnitTests using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; - using Spekt.TestLogger; using Spekt.TestLogger.UnitTests.TestDoubles; + using Spekt.TestLogger.VSTest; public class TestRunSimulator : IDisposable { diff --git a/test/TestLogger.UnitTests/TestRunStartWorkflowTests.cs b/test/TestLogger.VSTest.UnitTests/TestRunStartWorkflowTests.cs similarity index 96% rename from test/TestLogger.UnitTests/TestRunStartWorkflowTests.cs rename to test/TestLogger.VSTest.UnitTests/TestRunStartWorkflowTests.cs index b5d9ba16..e2bef08d 100644 --- a/test/TestLogger.UnitTests/TestRunStartWorkflowTests.cs +++ b/test/TestLogger.VSTest.UnitTests/TestRunStartWorkflowTests.cs @@ -9,6 +9,7 @@ namespace Spekt.TestLogger.UnitTests using Microsoft.VisualStudio.TestTools.UnitTesting; using Spekt.TestLogger.Core; using Spekt.TestLogger.UnitTests.TestDoubles; + using Spekt.TestLogger.VSTest.Core; [TestClass] public class TestRunStartWorkflowTests @@ -77,7 +78,8 @@ public void StartShouldRecordStartTimeOfTestRun() public void StartShouldUpdateTestRunConfiguration() { var loggerEvents = new MockTestLoggerEvents(); - var run = new TestRunBuilder().Subscribe(loggerEvents).Build(); + var run = new TestRunBuilder().Build(); + run.Subscribe(loggerEvents); loggerEvents.RaiseTestRunStart(this.testRunCriteria); diff --git a/test/TestLogger.UnitTests/Utilities/AttachmentSetExtensionsTests.cs b/test/TestLogger.VSTest.UnitTests/Utilities/AttachmentSetExtensionsTests.cs similarity index 98% rename from test/TestLogger.UnitTests/Utilities/AttachmentSetExtensionsTests.cs rename to test/TestLogger.VSTest.UnitTests/Utilities/AttachmentSetExtensionsTests.cs index 4475444e..1cab4873 100644 --- a/test/TestLogger.UnitTests/Utilities/AttachmentSetExtensionsTests.cs +++ b/test/TestLogger.VSTest.UnitTests/Utilities/AttachmentSetExtensionsTests.cs @@ -8,6 +8,7 @@ namespace Spekt.TestLogger.UnitTests.Utilities using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; using Spekt.TestLogger.Utilities; + using Spekt.TestLogger.VSTest.Utilities; [TestClass] public class AttachmentSetExtensionsTests diff --git a/test/Xunit.Xml.TestLogger.AcceptanceTests/AcceptanceTestCollection.cs b/test/Xunit.Xml.TestLogger.AcceptanceTests/AcceptanceTestCollection.cs new file mode 100644 index 00000000..a37cb777 --- /dev/null +++ b/test/Xunit.Xml.TestLogger.AcceptanceTests/AcceptanceTestCollection.cs @@ -0,0 +1,15 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Xunit.Xml.TestLogger.AcceptanceTests +{ + using Xunit; + + // Fixtures spawn dotnet test subprocesses that share mutable state + // (test/assets/global.json selects the test runner per leg), so all + // fixture-bound test classes must run sequentially in one collection. + [CollectionDefinition("Acceptance")] + public class AcceptanceTestCollection : ICollectionFixture, ICollectionFixture + { + } +} diff --git a/test/Xunit.Xml.TestLogger.AcceptanceTests/NoTestSdkAcceptanceTests.cs b/test/Xunit.Xml.TestLogger.AcceptanceTests/NoTestSdkAcceptanceTests.cs new file mode 100644 index 00000000..21eeb7a2 --- /dev/null +++ b/test/Xunit.Xml.TestLogger.AcceptanceTests/NoTestSdkAcceptanceTests.cs @@ -0,0 +1,65 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Xunit.Xml.TestLogger.AcceptanceTests +{ + using System.IO; + using System.Xml; + using Xunit; + + /// + /// Validates running the MTP logger on a test project without a + /// Microsoft.NET.Test.Sdk reference (see issue #229). + /// + [Collection("Acceptance")] + public class NoTestSdkAcceptanceTests + { + private const string TargetFrameworkVersion = "net8.0"; + private const string ObjectModelAssembly = "Microsoft.VisualStudio.TestPlatform.ObjectModel.dll"; + + private readonly NoTestSdkFixture fixture; + + public NoTestSdkAcceptanceTests(NoTestSdkFixture fixture) + { + this.fixture = fixture; + } + + [Fact] + public void MtpRunWithoutTestSdkShouldProduceResultsFile() + { + Assert.True(File.Exists(this.fixture.ResultsFile)); + } + + [Fact] + public void MtpRunWithoutTestSdkShouldReportTestCounts() + { + var resultsXml = new XmlDocument(); + resultsXml.Load(this.fixture.ResultsFile); + var assemblyNode = resultsXml.SelectSingleNode("/assemblies/assembly"); + + Assert.NotNull(assemblyNode); + Assert.Equal("3", assemblyNode.Attributes["total"].Value); + Assert.Equal("2", assemblyNode.Attributes["passed"].Value); + Assert.Equal("1", assemblyNode.Attributes["failed"].Value); + } + + [Fact] + public void MtpRunWithoutTestSdkShouldNotDeployTestPlatformObjectModel() + { +#if DEBUG + var config = "Debug"; +#else + var config = "Release"; +#endif + var objectModelPath = Path.Combine( + this.fixture.AssetDirectory, + "bin", + config, + "mtp", + TargetFrameworkVersion, + ObjectModelAssembly); + + Assert.False(File.Exists(objectModelPath), "Microsoft.VisualStudio.TestPlatform.ObjectModel must not be deployed for MTP-only runs (issue #229)."); + } + } +} diff --git a/test/Xunit.Xml.TestLogger.AcceptanceTests/NoTestSdkFixture.cs b/test/Xunit.Xml.TestLogger.AcceptanceTests/NoTestSdkFixture.cs new file mode 100644 index 00000000..dd34d8ea --- /dev/null +++ b/test/Xunit.Xml.TestLogger.AcceptanceTests/NoTestSdkFixture.cs @@ -0,0 +1,31 @@ +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Xunit.Xml.TestLogger.AcceptanceTests +{ + using global::TestLogger.Fixtures; + using Xunit; + + public class NoTestSdkFixture + { + private const string AssetName = "Xunit.Xml.TestLogger.NoTestSdk.Tests"; + private const string ResultsFileName = "test-results-mtp.xml"; + + public NoTestSdkFixture() + { + // MTP-only asset without Microsoft.NET.Test.Sdk (see issue #229). + var mtpLoggerArgs = $"--report-spekt-xunit --report-spekt-xunit-filename {ResultsFileName}"; + this.ResultsFile = global::TestLogger.Fixtures.DotnetTestFixture + .Create() + .WithNoBuild() + .Execute(AssetName, mtpLoggerArgs, collectCoverage: false, resultsFileName: ResultsFileName, isMTP: true); + + Assert.False(string.IsNullOrEmpty(this.ResultsFile), "MTP results file cannot be null"); + this.AssetDirectory = AssetName.ToAssetDirectoryPath(); + } + + public string ResultsFile { get; } + + public string AssetDirectory { get; } + } +} diff --git a/test/Xunit.Xml.TestLogger.AcceptanceTests/TestResultsXmlTests.cs b/test/Xunit.Xml.TestLogger.AcceptanceTests/TestResultsXmlTests.cs index 7fed747a..7fc62312 100644 --- a/test/Xunit.Xml.TestLogger.AcceptanceTests/TestResultsXmlTests.cs +++ b/test/Xunit.Xml.TestLogger.AcceptanceTests/TestResultsXmlTests.cs @@ -14,7 +14,8 @@ namespace Xunit.Xml.TestLogger.AcceptanceTests using System.Xml; using Xunit; - public class TestResultsXmlTests : IClassFixture + [Collection("Acceptance")] + public class TestResultsXmlTests { private const string AssembliesElement = @"/assemblies"; private const string AssemblyElement = @"/assemblies/assembly"; diff --git a/test/Xunit.Xml.TestLogger.AcceptanceTests/TestRunFixture.cs b/test/Xunit.Xml.TestLogger.AcceptanceTests/TestRunFixture.cs index 0043cf42..a56d68c4 100644 --- a/test/Xunit.Xml.TestLogger.AcceptanceTests/TestRunFixture.cs +++ b/test/Xunit.Xml.TestLogger.AcceptanceTests/TestRunFixture.cs @@ -16,14 +16,13 @@ public TestRunFixture() var vstestLoggerArgs = "xunit;LogFilePath=test-results-vstest.xml"; var vstestResultsFile = global::TestLogger.Fixtures.DotnetTestFixture .Create() - .WithBuild() .Execute("Xunit.Xml.TestLogger.NetCore.Tests", vstestLoggerArgs, collectCoverage: false, resultsFileName: "test-results-vstest.xml", isMTP: false); // Run MTP tests var mtpLoggerArgs = "--report-spekt-xunit --report-spekt-xunit-filename test-results-mtp.xml"; var mtpResultsFile = global::TestLogger.Fixtures.DotnetTestFixture .Create() - .WithBuild() + .WithNoBuild() .Execute("Xunit.Xml.TestLogger.NetCore.Tests", mtpLoggerArgs, collectCoverage: false, resultsFileName: "test-results-mtp.xml", isMTP: true); Assert.False(string.IsNullOrEmpty(vstestResultsFile), "VSTest results file cannot be null"); diff --git a/test/Xunit.Xml.TestLogger.AcceptanceTests/Xunit.Xml.TestLogger.AcceptanceTests.csproj b/test/Xunit.Xml.TestLogger.AcceptanceTests/Xunit.Xml.TestLogger.AcceptanceTests.csproj index 11af767a..cad26f80 100644 --- a/test/Xunit.Xml.TestLogger.AcceptanceTests/Xunit.Xml.TestLogger.AcceptanceTests.csproj +++ b/test/Xunit.Xml.TestLogger.AcceptanceTests/Xunit.Xml.TestLogger.AcceptanceTests.csproj @@ -30,10 +30,16 @@ + + + + + + diff --git a/test/Xunit.Xml.TestLogger.AcceptanceTests/XunitTestLoggerNetFullAcceptanceTests.cs b/test/Xunit.Xml.TestLogger.AcceptanceTests/XunitTestLoggerNetFullAcceptanceTests.cs index 3e62a45e..c62726e5 100644 --- a/test/Xunit.Xml.TestLogger.AcceptanceTests/XunitTestLoggerNetFullAcceptanceTests.cs +++ b/test/Xunit.Xml.TestLogger.AcceptanceTests/XunitTestLoggerNetFullAcceptanceTests.cs @@ -42,7 +42,6 @@ public static void SuiteInitialize(TestContext context) // Enable reporting of internal properties in the adapter using runsettings _ = DotnetTestFixture .Create() - .WithBuild() .Execute(AssetName, loggerArgs, collectCoverage: false, "test-results.xml"); } diff --git a/test/assets/Directory.Build.props b/test/assets/Directory.Build.props index 7555e135..4f0b9c45 100644 --- a/test/assets/Directory.Build.props +++ b/test/assets/Directory.Build.props @@ -21,6 +21,28 @@ 3.0.4 + + + Debug + + + + + obj\mtp\ + bin\$(Configuration)\mtp\ + $(DefaultItemExcludes);obj\vstest\** + + + obj\vstest\ + bin\$(Configuration)\vstest\ + $(DefaultItemExcludes);obj\mtp\** + + true $(DefineConstants);USES_XUNIT3 diff --git a/test/assets/JUnit.Xml.TestLogger.NetCore.Tests/JUnit.Xml.TestLogger.NetCore.Tests.csproj b/test/assets/JUnit.Xml.TestLogger.NetCore.Tests/JUnit.Xml.TestLogger.NetCore.Tests.csproj index e74a5831..c9a2b92a 100644 --- a/test/assets/JUnit.Xml.TestLogger.NetCore.Tests/JUnit.Xml.TestLogger.NetCore.Tests.csproj +++ b/test/assets/JUnit.Xml.TestLogger.NetCore.Tests/JUnit.Xml.TestLogger.NetCore.Tests.csproj @@ -3,12 +3,16 @@ net8.0 + + exe + false - + + diff --git a/test/assets/JUnit.Xml.TestLogger.NetFull.Tests/JUnit.Xml.TestLogger.NetFull.Tests.csproj b/test/assets/JUnit.Xml.TestLogger.NetFull.Tests/JUnit.Xml.TestLogger.NetFull.Tests.csproj index 6cde5767..b29c9daa 100644 --- a/test/assets/JUnit.Xml.TestLogger.NetFull.Tests/JUnit.Xml.TestLogger.NetFull.Tests.csproj +++ b/test/assets/JUnit.Xml.TestLogger.NetFull.Tests/JUnit.Xml.TestLogger.NetFull.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/assets/JUnit.Xml.TestLogger.NetMulti.Tests/JUnit.Xml.TestLogger.NetMulti.Tests.csproj b/test/assets/JUnit.Xml.TestLogger.NetMulti.Tests/JUnit.Xml.TestLogger.NetMulti.Tests.csproj index 6f1cff81..d4c5210e 100644 --- a/test/assets/JUnit.Xml.TestLogger.NetMulti.Tests/JUnit.Xml.TestLogger.NetMulti.Tests.csproj +++ b/test/assets/JUnit.Xml.TestLogger.NetMulti.Tests/JUnit.Xml.TestLogger.NetMulti.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/test/assets/JUnit.Xml.TestLogger.XUnit.NetCore.Tests/JUnit.Xml.TestLogger.XUnit.NetCore.Tests.csproj b/test/assets/JUnit.Xml.TestLogger.XUnit.NetCore.Tests/JUnit.Xml.TestLogger.XUnit.NetCore.Tests.csproj index 05e26f43..486f4731 100644 --- a/test/assets/JUnit.Xml.TestLogger.XUnit.NetCore.Tests/JUnit.Xml.TestLogger.XUnit.NetCore.Tests.csproj +++ b/test/assets/JUnit.Xml.TestLogger.XUnit.NetCore.Tests/JUnit.Xml.TestLogger.XUnit.NetCore.Tests.csproj @@ -6,7 +6,8 @@ - + + diff --git a/test/assets/NUnit.Xml.TestLogger.NetCore.Tests/NUnit.Xml.TestLogger.NetCore.Tests.csproj b/test/assets/NUnit.Xml.TestLogger.NetCore.Tests/NUnit.Xml.TestLogger.NetCore.Tests.csproj index 405c06fb..3e8ace39 100644 --- a/test/assets/NUnit.Xml.TestLogger.NetCore.Tests/NUnit.Xml.TestLogger.NetCore.Tests.csproj +++ b/test/assets/NUnit.Xml.TestLogger.NetCore.Tests/NUnit.Xml.TestLogger.NetCore.Tests.csproj @@ -3,12 +3,16 @@ net8.0 + + exe + false - + + diff --git a/test/assets/NUnit.Xml.TestLogger.NetFull.Tests/NUnit.Xml.TestLogger.NetFull.Tests.csproj b/test/assets/NUnit.Xml.TestLogger.NetFull.Tests/NUnit.Xml.TestLogger.NetFull.Tests.csproj index a20aeb55..81634cf4 100644 --- a/test/assets/NUnit.Xml.TestLogger.NetFull.Tests/NUnit.Xml.TestLogger.NetFull.Tests.csproj +++ b/test/assets/NUnit.Xml.TestLogger.NetFull.Tests/NUnit.Xml.TestLogger.NetFull.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/test/assets/NUnit.Xml.TestLogger.NetMulti.Tests/NUnit.Xml.TestLogger.NetMulti.Tests.csproj b/test/assets/NUnit.Xml.TestLogger.NetMulti.Tests/NUnit.Xml.TestLogger.NetMulti.Tests.csproj index 9ab09261..24ddd25c 100644 --- a/test/assets/NUnit.Xml.TestLogger.NetMulti.Tests/NUnit.Xml.TestLogger.NetMulti.Tests.csproj +++ b/test/assets/NUnit.Xml.TestLogger.NetMulti.Tests/NUnit.Xml.TestLogger.NetMulti.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/test/assets/Xunit.Xml.TestLogger.NetCore.Tests/Xunit.Xml.TestLogger.NetCore.Tests.csproj b/test/assets/Xunit.Xml.TestLogger.NetCore.Tests/Xunit.Xml.TestLogger.NetCore.Tests.csproj index cccb57ce..3984d818 100644 --- a/test/assets/Xunit.Xml.TestLogger.NetCore.Tests/Xunit.Xml.TestLogger.NetCore.Tests.csproj +++ b/test/assets/Xunit.Xml.TestLogger.NetCore.Tests/Xunit.Xml.TestLogger.NetCore.Tests.csproj @@ -6,7 +6,8 @@ - + + diff --git a/test/assets/Xunit.Xml.TestLogger.NetFull.Tests/Xunit.Xml.TestLogger.NetFull.Tests.csproj b/test/assets/Xunit.Xml.TestLogger.NetFull.Tests/Xunit.Xml.TestLogger.NetFull.Tests.csproj index 17a339e6..e314aa0b 100644 --- a/test/assets/Xunit.Xml.TestLogger.NetFull.Tests/Xunit.Xml.TestLogger.NetFull.Tests.csproj +++ b/test/assets/Xunit.Xml.TestLogger.NetFull.Tests/Xunit.Xml.TestLogger.NetFull.Tests.csproj @@ -7,7 +7,7 @@ - + diff --git a/test/assets/Xunit.Xml.TestLogger.NoTestSdk.Tests/UnitTest1.cs b/test/assets/Xunit.Xml.TestLogger.NoTestSdk.Tests/UnitTest1.cs new file mode 100644 index 00000000..63a54d41 --- /dev/null +++ b/test/assets/Xunit.Xml.TestLogger.NoTestSdk.Tests/UnitTest1.cs @@ -0,0 +1,24 @@ +using Xunit; + +namespace Xunit.Xml.TestLogger.NoTestSdk.Tests +{ + public class UnitTest1 + { + [Fact] + public void PassTest11() + { + } + + [Fact] + public void PassTest12() + { + Assert.Equal(2, 2); + } + + [Fact] + public void FailTest11() + { + Assert.False(true); + } + } +} diff --git a/test/assets/Xunit.Xml.TestLogger.NoTestSdk.Tests/Xunit.Xml.TestLogger.NoTestSdk.Tests.csproj b/test/assets/Xunit.Xml.TestLogger.NoTestSdk.Tests/Xunit.Xml.TestLogger.NoTestSdk.Tests.csproj new file mode 100644 index 00000000..31a3d489 --- /dev/null +++ b/test/assets/Xunit.Xml.TestLogger.NoTestSdk.Tests/Xunit.Xml.TestLogger.NoTestSdk.Tests.csproj @@ -0,0 +1,32 @@ + + + + + net8.0 + exe + + true + + + + + + + + + + + + + + + XunitXml.TestLogger + Spekt.TestReporter.Xunit.TestingPlatformBuilderHook + + + + diff --git a/testlogger.slnx b/testlogger.slnx index bbb78559..9e5b438c 100644 --- a/testlogger.slnx +++ b/testlogger.slnx @@ -6,13 +6,17 @@ - + + - + + - + + - + + @@ -24,7 +28,8 @@ - + + diff --git a/version.txt b/version.txt index ae9a76b9..8104cabd 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -8.0.0 +8.1.0 From 50516816511f6d64ede96d4cb1841c360db06ff0 Mon Sep 17 00:00:00 2001 From: Arun Mahapatra Date: Thu, 3 Sep 2026 21:40:27 +0530 Subject: [PATCH 2/2] fix: use exec to pre-build test assets --- .../JUnit.Xml.TestLogger.AcceptanceTests.csproj | 5 +++-- .../NUnit.Xml.TestLogger.AcceptanceTests.csproj | 5 +++-- .../TestLogger.AcceptanceTests.csproj | 3 ++- test/TestLogger.PackageTests/TestLogger.PackageTests.csproj | 4 +++- .../Xunit.Xml.TestLogger.AcceptanceTests.csproj | 5 +++-- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnit.Xml.TestLogger.AcceptanceTests.csproj b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnit.Xml.TestLogger.AcceptanceTests.csproj index c07d632b..4b40d54e 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnit.Xml.TestLogger.AcceptanceTests.csproj +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnit.Xml.TestLogger.AcceptanceTests.csproj @@ -37,8 +37,9 @@ - - + + + diff --git a/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnit.Xml.TestLogger.AcceptanceTests.csproj b/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnit.Xml.TestLogger.AcceptanceTests.csproj index c99b2754..f816d7a2 100644 --- a/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnit.Xml.TestLogger.AcceptanceTests.csproj +++ b/test/NUnit.Xml.TestLogger.AcceptanceTests/NUnit.Xml.TestLogger.AcceptanceTests.csproj @@ -34,8 +34,9 @@ - - + + + diff --git a/test/TestLogger.AcceptanceTests/TestLogger.AcceptanceTests.csproj b/test/TestLogger.AcceptanceTests/TestLogger.AcceptanceTests.csproj index 695fbdfd..6f1ba13f 100644 --- a/test/TestLogger.AcceptanceTests/TestLogger.AcceptanceTests.csproj +++ b/test/TestLogger.AcceptanceTests/TestLogger.AcceptanceTests.csproj @@ -39,7 +39,8 @@ - + + diff --git a/test/TestLogger.PackageTests/TestLogger.PackageTests.csproj b/test/TestLogger.PackageTests/TestLogger.PackageTests.csproj index 6821a109..192cef00 100644 --- a/test/TestLogger.PackageTests/TestLogger.PackageTests.csproj +++ b/test/TestLogger.PackageTests/TestLogger.PackageTests.csproj @@ -37,7 +37,9 @@ - + + + diff --git a/test/Xunit.Xml.TestLogger.AcceptanceTests/Xunit.Xml.TestLogger.AcceptanceTests.csproj b/test/Xunit.Xml.TestLogger.AcceptanceTests/Xunit.Xml.TestLogger.AcceptanceTests.csproj index cad26f80..16be5f7a 100644 --- a/test/Xunit.Xml.TestLogger.AcceptanceTests/Xunit.Xml.TestLogger.AcceptanceTests.csproj +++ b/test/Xunit.Xml.TestLogger.AcceptanceTests/Xunit.Xml.TestLogger.AcceptanceTests.csproj @@ -38,8 +38,9 @@ - - + + +