diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f788f0266f..2874056945 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,6 +1,4 @@ # https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners -* @Evangelink - src/Package/MSTest.Sdk @MarcoRossignoli -src/Platform @MarcoRossignoli @Evangelink \ No newline at end of file +src/Platform @MarcoRossignoli @Evangelink diff --git a/.gitignore b/.gitignore index 32f4784062..65109106dc 100644 --- a/.gitignore +++ b/.gitignore @@ -142,3 +142,6 @@ project.fragment.lock.json # Playground project (use force add if a change is really worth it) /samples/Playground/ + +# jetbrains temp +.idea/ \ No newline at end of file diff --git a/.markdownlint.json b/.markdownlint.json index e70a5096b2..80834d3dea 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -7,6 +7,9 @@ "MD024": { "siblings_only": true }, + "MD033": { + "allowed_elements": ["a"] + }, "MD046": { "style": "fenced" }, diff --git a/Directory.Build.Local.props b/Directory.Build.Local.props deleted file mode 100644 index 656f35a3a3..0000000000 --- a/Directory.Build.Local.props +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - true - - - - - - preview - enable - true - - $(NoWarn),1573,1591 - - - - - net462 - uap10.0.16299 - net6.0-windows10.0.18362.0 - net8.0 - - netcoreapp3.1;net6.0;net7.0;net8.0 - net6.0;net7.0;net8.0 - - - - - - false - - true - embedded - - true - - - - - $(CopyrightMicrosoft) - MIT - true - - - - - Microsoft - - - - - TestingPlatformRunner - - true - 0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7 - - - diff --git a/Directory.Build.Local.targets b/Directory.Build.Local.targets deleted file mode 100644 index dbeb8b8d9a..0000000000 --- a/Directory.Build.Local.targets +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - 14.0.0.0 - 14.0.0.0 - - - - - true - $(DefineConstants);ENABLE_CODECOVERAGE - $(PlatformTarget) - x64 - $(MSBuildProjectName)_$(TargetFramework)_$(Configuration)_$(Architecture) - - $(TestRunnerAdditionalArguments) --coverage --coverage-settings $(RepoRoot)eng/coverage.config --coverage-output $(ModuleName).coverage - - - diff --git a/Directory.Build.props b/Directory.Build.props index aabb126076..8d175de01b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,10 +1,67 @@ - - + + - - + + true + + + + + + preview + enable + true + + $(NoWarn),1573,1591 + + + + + net462 + uap10.0.16299 + net6.0-windows10.0.18362.0 + net8.0 + + netcoreapp3.1;net6.0;net7.0;net8.0 + net6.0;net7.0;net8.0 + + + + + + false + + true + embedded + + true + + + + + $(CopyrightMicrosoft) MIT + + + + Microsoft + + + + + TestingPlatformRunner + + true + 0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7 + + diff --git a/Directory.Build.targets b/Directory.Build.targets index e95c97089d..4220fbc3f6 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,6 +1,14 @@ - - + + + + + 14.0.0.0 + 14.0.0.0 + diff --git a/Directory.Packages.props b/Directory.Packages.props index f80853d6f7..c4af76c198 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,26 +5,29 @@ true - 8.0.0-preview.6.24214.1 - - 17.9.0 + 8.0.1 + 17.10.0-preview-24127-03 + 3.11.0-beta1.24318.1 + 3.11.0 + $(MicrosoftCodeAnalysisAnalyzersVersion) + $(MicrosoftCodeAnalysisPublicApiAnalyzersVersion) 6.2.14 - + + 17.10.0 + 1.44.0 4.3.1 4.3.1 - 3.11.0-beta1.24072.1 - 3.11.0 - $(MicrosoftCodeAnalysisAnalyzersVersion) - $(MicrosoftCodeAnalysisPublicApiAnalyzersVersion) - 1.42.0 - 1.1.2-beta1.24074.2 + 1.1.3-beta1.24352.1 - + + + + @@ -33,20 +36,18 @@ - + - - - - - - + + + + - - + + @@ -55,17 +56,18 @@ - + + - + - - + + - + diff --git a/MSTest.slnf b/MSTest.slnf new file mode 100644 index 0000000000..5985887daf --- /dev/null +++ b/MSTest.slnf @@ -0,0 +1,60 @@ +{ + "solution": { + "path": "TestFx.sln", + "projects": [ + "samples\\FxExtensibility\\FxExtensibility.csproj", + "samples\\Playground\\Playground.csproj", + "src\\Adapter\\MSTest.TestAdapter\\MSTest.TestAdapter.csproj", + "src\\Adapter\\MSTestAdapter.PlatformServices\\MSTestAdapter.PlatformServices.csproj", + "src\\Analyzers\\MSTest.Analyzers.CodeFixes\\MSTest.Analyzers.CodeFixes.csproj", + "src\\Analyzers\\MSTest.Analyzers.Package\\MSTest.Analyzers.Package.csproj", + "src\\Analyzers\\MSTest.Analyzers\\MSTest.Analyzers.csproj", + "src\\Package\\MSTest.Internal.TestFx.Documentation\\MSTest.Internal.TestFx.Documentation.csproj", + "src\\Package\\MSTest.Sdk\\MSTest.Sdk.csproj", + "src\\Package\\MSTest\\MSTest.csproj", + "src\\TestFramework\\TestFramework.Extensions\\TestFramework.Extensions.csproj", + "src\\TestFramework\\TestFramework\\TestFramework.csproj", + "test\\IntegrationTests\\MSTest.Acceptance.IntegrationTests\\MSTest.Acceptance.IntegrationTests.csproj", + "test\\IntegrationTests\\MSTest.IntegrationTests\\MSTest.IntegrationTests.csproj", + "test\\IntegrationTests\\MSTest.VstestConsoleWrapper.IntegrationTests\\MSTest.VstestConsoleWrapper.IntegrationTests.csproj", + "test\\IntegrationTests\\PlatformServices.Desktop.IntegrationTests\\PlatformServices.Desktop.IntegrationTests.csproj", + "test\\IntegrationTests\\TestAssets\\ClsTestProject\\ClsTestProject.csproj", + "test\\IntegrationTests\\TestAssets\\DataRowTestProject\\DataRowTestProject.csproj", + "test\\IntegrationTests\\TestAssets\\DataSourceTestProject\\DataSourceTestProject.csproj", + "test\\IntegrationTests\\TestAssets\\DeploymentTestProject.Never\\DeploymentTestProject.Never.csproj", + "test\\IntegrationTests\\TestAssets\\DeploymentTestProject.PreserveNewest\\DeploymentTestProject.PreserveNewest.csproj", + "test\\IntegrationTests\\TestAssets\\DesktopTestProjectx64Debug\\DesktopTestProjectx64Debug.csproj", + "test\\IntegrationTests\\TestAssets\\DesktopTestProjectx64Release\\DesktopTestProjectx64Release.csproj", + "test\\IntegrationTests\\TestAssets\\DesktopTestProjectx86Debug\\DesktopTestProjectx86Debug.csproj", + "test\\IntegrationTests\\TestAssets\\DesktopTestProjectx86Release\\DesktopTestProjectx86Release.csproj", + "test\\IntegrationTests\\TestAssets\\DiscoverInternalsProject\\DiscoverInternalsProject.csproj", + "test\\IntegrationTests\\TestAssets\\DoNotParallelizeTestProject\\DoNotParallelizeTestProject.csproj", + "test\\IntegrationTests\\TestAssets\\DynamicDataTestProject\\DynamicDataTestProject.csproj", + "test\\IntegrationTests\\TestAssets\\FSharpTestProject\\FSharpTestProject.fsproj", + "test\\IntegrationTests\\TestAssets\\FixturesTestProject\\FixturesTestProject.csproj", + "test\\IntegrationTests\\TestAssets\\FxExtensibilityTestProject\\FxExtensibilityTestProject.csproj", + "test\\IntegrationTests\\TestAssets\\HierarchyProject\\HierarchyProject.csproj", + "test\\IntegrationTests\\TestAssets\\LibProjectReferencedByDataSourceTest\\LibProjectReferencedByDataSourceTest.csproj", + "test\\IntegrationTests\\TestAssets\\OutputTestProject\\OutputTestProject.csproj", + "test\\IntegrationTests\\TestAssets\\ParallelTestClass\\ParallelClassesTestProject.csproj", + "test\\IntegrationTests\\TestAssets\\ParallelTestMethods\\ParallelMethodsTestProject.csproj", + "test\\IntegrationTests\\TestAssets\\SampleFrameworkExtensions\\SampleFrameworkExtensions.csproj", + "test\\IntegrationTests\\TestAssets\\SampleProjectForAssemblyResolution\\SampleProjectForAssemblyResolution.csproj", + "test\\IntegrationTests\\TestAssets\\SuiteLifeCycleTestProject\\SuiteLifeCycleTestProject.csproj", + "test\\IntegrationTests\\TestAssets\\TestIdProject.DefaultStrategy\\TestIdProject.DefaultStrategy.csproj", + "test\\IntegrationTests\\TestAssets\\TestIdProject.DisplayNameStrategy\\TestIdProject.DisplayNameStrategy.csproj", + "test\\IntegrationTests\\TestAssets\\TestIdProject.FullyQualifiedTestStrategy\\TestIdProject.FullyQualifiedStrategy.csproj", + "test\\IntegrationTests\\TestAssets\\TestIdProject.LegacyStrategy\\TestIdProject.LegacyStrategy.csproj", + "test\\IntegrationTests\\TestAssets\\TestProject\\TestProjectForDiscovery.csproj", + "test\\IntegrationTests\\TestAssets\\TimeoutTestProject\\TimeoutTestProject.csproj", + "test\\Performance\\MSTest.Performance.Runner\\MSTest.Performance.Runner.csproj", + "test\\UnitTests\\MSTest.Analyzers.UnitTests\\MSTest.Analyzers.UnitTests.csproj", + "test\\UnitTests\\MSTestAdapter.PlatformServices.UnitTests\\MSTestAdapter.PlatformServices.UnitTests.csproj", + "test\\UnitTests\\MSTestAdapter.UnitTests\\MSTestAdapter.UnitTests.csproj", + "test\\UnitTests\\TestFramework.UnitTests\\TestFramework.UnitTests.csproj", + "test\\Utilities\\Automation.CLI\\Automation.CLI.csproj", + "test\\Utilities\\Microsoft.Testing.TestInfrastructure\\Microsoft.Testing.TestInfrastructure.csproj", + "test\\Utilities\\TestFramework.ForTestingMSTest\\TestFramework.ForTestingMSTest.csproj" + ] + } +} \ No newline at end of file diff --git a/Microsoft.Testing.Platform.slnf b/Microsoft.Testing.Platform.slnf new file mode 100644 index 0000000000..1595904e3a --- /dev/null +++ b/Microsoft.Testing.Platform.slnf @@ -0,0 +1,22 @@ +{ + "solution": { + "path": "TestFx.sln", + "projects": [ + "samples\\Playground\\Playground.csproj", + "src\\Platform\\Microsoft.Testing.Extensions.CrashDump\\Microsoft.Testing.Extensions.CrashDump.csproj", + "src\\Platform\\Microsoft.Testing.Extensions.HangDump\\Microsoft.Testing.Extensions.HangDump.csproj", + "src\\Platform\\Microsoft.Testing.Extensions.Telemetry\\Microsoft.Testing.Extensions.Telemetry.csproj", + "src\\Platform\\Microsoft.Testing.Extensions.TrxReport.Abstractions\\Microsoft.Testing.Extensions.TrxReport.Abstractions.csproj", + "src\\Platform\\Microsoft.Testing.Extensions.TrxReport\\Microsoft.Testing.Extensions.TrxReport.csproj", + "src\\Platform\\Microsoft.Testing.Extensions.VSTestBridge\\Microsoft.Testing.Extensions.VSTestBridge.csproj", + "src\\Platform\\Microsoft.Testing.Platform.MSBuild\\Microsoft.Testing.Platform.MSBuild.csproj", + "src\\Platform\\Microsoft.Testing.Platform\\Microsoft.Testing.Platform.csproj", + "test\\IntegrationTests\\Microsoft.Testing.Platform.Acceptance.IntegrationTests\\Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj", + "test\\UnitTests\\Microsoft.Testing.Extensions.UnitTests\\Microsoft.Testing.Extensions.UnitTests.csproj", + "test\\UnitTests\\Microsoft.Testing.Extensions.VSTestBridge.UnitTests\\Microsoft.Testing.Extensions.VSTestBridge.UnitTests.csproj", + "test\\UnitTests\\Microsoft.Testing.Platform.MSBuild.UnitTests\\Microsoft.Testing.Platform.MSBuild.UnitTests.csproj", + "test\\UnitTests\\Microsoft.Testing.Platform.UnitTests\\Microsoft.Testing.Platform.UnitTests.csproj", + "test\\Utilities\\Microsoft.Testing.TestInfrastructure\\Microsoft.Testing.TestInfrastructure.csproj" + ] + } +} \ No newline at end of file diff --git a/TestFx.sln b/TestFx.sln index cc9f95b2d5..0baa18d4f6 100644 --- a/TestFx.sln +++ b/TestFx.sln @@ -22,6 +22,8 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A9596292-7E67-4566-9096-143DDAA4E8D8}" ProjectSection(SolutionItems) = preProject test\.editorconfig = test\.editorconfig + test\Directory.Build.props = test\Directory.Build.props + test\Directory.Build.targets = test\Directory.Build.targets EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestFramework.Extensions", "src\TestFramework\TestFramework.Extensions\TestFramework.Extensions.csproj", "{DF131865-84EE-4540-8112-E88ACEBDEA09}" @@ -46,8 +48,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitignore = .gitignore - Directory.Build.Local.props = Directory.Build.Local.props - Directory.Build.Local.targets = Directory.Build.Local.targets Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets Directory.Packages.props = Directory.Packages.props @@ -67,8 +67,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eng", "eng", "{FE0DF239-0D8 ProjectSection(SolutionItems) = preProject eng\AfterSolutionBuild.targets = eng\AfterSolutionBuild.targets eng\Analyzers.props = eng\Analyzers.props + eng\Build.props = eng\Build.props eng\coverage.config = eng\coverage.config - eng\Install-WindowsSDK.ps1 = eng\Install-WindowsSDK.ps1 + eng\install-windows-sdk.ps1 = eng\install-windows-sdk.ps1 eng\stylecop.json = eng\stylecop.json eng\verify-nupkgs.ps1 = eng\verify-nupkgs.ps1 eng\Version.Details.xml = eng\Version.Details.xml @@ -169,7 +170,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestingPlatformRunner", "Te eng\TestingPlatformRunner\TestingPlatformRunner.targets = eng\TestingPlatformRunner\TestingPlatformRunner.targets EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1 - Runner", "1 - Runner", "{6AEE1440-FDF0-4729-8196-B24D0E333550}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1 - Platform & Extensions", "1 - Platform & Extensions", "{6AEE1440-FDF0-4729-8196-B24D0E333550}" + ProjectSection(SolutionItems) = preProject + src\Platform\Directory.Build.props = src\Platform\Directory.Build.props + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Platform", "src\Platform\Microsoft.Testing.Platform\Microsoft.Testing.Platform.csproj", "{48FAB979-8DA5-492E-8B3F-5DBBE82F659A}" EndProject @@ -201,6 +205,28 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Playground", "samples\Playg EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.Acceptance.IntegrationTests", "test\IntegrationTests\MSTest.Acceptance.IntegrationTests\MSTest.Acceptance.IntegrationTests.csproj", "{BCB42780-C559-40B6-8C4A-85EBC464AAA8}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FixturesTestProject", "test\IntegrationTests\TestAssets\FixturesTestProject\FixturesTestProject.csproj", "{A7D0995D-0516-4975-ABBD-EB93E1B79292}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Extensions.TrxReport.Abstractions", "src\Platform\Microsoft.Testing.Extensions.TrxReport.Abstractions\Microsoft.Testing.Extensions.TrxReport.Abstractions.csproj", "{9164E0BA-0846-4839-BA0F-C25F5FBE056C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Extensions.TrxReport", "src\Platform\Microsoft.Testing.Extensions.TrxReport\Microsoft.Testing.Extensions.TrxReport.csproj", "{29B9F157-3733-471E-A11E-A5FF3C6D1348}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Extensions.VSTestBridge", "src\Platform\Microsoft.Testing.Extensions.VSTestBridge\Microsoft.Testing.Extensions.VSTestBridge.csproj", "{60763BAA-C963-4858-8DA1-78DB92428865}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Platform.MSBuild", "src\Platform\Microsoft.Testing.Platform.MSBuild\Microsoft.Testing.Platform.MSBuild.csproj", "{1B30B69C-A4E3-4660-9CA8-140D0C34B4A5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Extensions.Telemetry", "src\Platform\Microsoft.Testing.Extensions.Telemetry\Microsoft.Testing.Extensions.Telemetry.csproj", "{BCA498E6-22C7-4E3F-8862-A7FAA06652D1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Extensions.CrashDump", "src\Platform\Microsoft.Testing.Extensions.CrashDump\Microsoft.Testing.Extensions.CrashDump.csproj", "{DFC9B46A-BFA7-407D-B872-7104C78A0787}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Extensions.HangDump", "src\Platform\Microsoft.Testing.Extensions.HangDump\Microsoft.Testing.Extensions.HangDump.csproj", "{8C743361-B796-4A92-BD69-3B5DD734BA6F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Extensions.UnitTests", "test\UnitTests\Microsoft.Testing.Extensions.UnitTests\Microsoft.Testing.Extensions.UnitTests.csproj", "{16FEFD31-B0D6-4291-B620-F902A16F39DC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Extensions.VSTestBridge.UnitTests", "test\UnitTests\Microsoft.Testing.Extensions.VSTestBridge.UnitTests\Microsoft.Testing.Extensions.VSTestBridge.UnitTests.csproj", "{573C617F-6BB2-403A-AD87-E00A7FD537F0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Platform.MSBuild.UnitTests", "test\UnitTests\Microsoft.Testing.Platform.MSBuild.UnitTests\Microsoft.Testing.Platform.MSBuild.UnitTests.csproj", "{F422398C-72CD-43EA-AC8E-E0DBD08E5563}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -427,6 +453,50 @@ Global {BCB42780-C559-40B6-8C4A-85EBC464AAA8}.Debug|Any CPU.Build.0 = Debug|Any CPU {BCB42780-C559-40B6-8C4A-85EBC464AAA8}.Release|Any CPU.ActiveCfg = Release|Any CPU {BCB42780-C559-40B6-8C4A-85EBC464AAA8}.Release|Any CPU.Build.0 = Release|Any CPU + {A7D0995D-0516-4975-ABBD-EB93E1B79292}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7D0995D-0516-4975-ABBD-EB93E1B79292}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7D0995D-0516-4975-ABBD-EB93E1B79292}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7D0995D-0516-4975-ABBD-EB93E1B79292}.Release|Any CPU.Build.0 = Release|Any CPU + {9164E0BA-0846-4839-BA0F-C25F5FBE056C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9164E0BA-0846-4839-BA0F-C25F5FBE056C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9164E0BA-0846-4839-BA0F-C25F5FBE056C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9164E0BA-0846-4839-BA0F-C25F5FBE056C}.Release|Any CPU.Build.0 = Release|Any CPU + {29B9F157-3733-471E-A11E-A5FF3C6D1348}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29B9F157-3733-471E-A11E-A5FF3C6D1348}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29B9F157-3733-471E-A11E-A5FF3C6D1348}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29B9F157-3733-471E-A11E-A5FF3C6D1348}.Release|Any CPU.Build.0 = Release|Any CPU + {60763BAA-C963-4858-8DA1-78DB92428865}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60763BAA-C963-4858-8DA1-78DB92428865}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60763BAA-C963-4858-8DA1-78DB92428865}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60763BAA-C963-4858-8DA1-78DB92428865}.Release|Any CPU.Build.0 = Release|Any CPU + {1B30B69C-A4E3-4660-9CA8-140D0C34B4A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B30B69C-A4E3-4660-9CA8-140D0C34B4A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B30B69C-A4E3-4660-9CA8-140D0C34B4A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B30B69C-A4E3-4660-9CA8-140D0C34B4A5}.Release|Any CPU.Build.0 = Release|Any CPU + {BCA498E6-22C7-4E3F-8862-A7FAA06652D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BCA498E6-22C7-4E3F-8862-A7FAA06652D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BCA498E6-22C7-4E3F-8862-A7FAA06652D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BCA498E6-22C7-4E3F-8862-A7FAA06652D1}.Release|Any CPU.Build.0 = Release|Any CPU + {DFC9B46A-BFA7-407D-B872-7104C78A0787}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DFC9B46A-BFA7-407D-B872-7104C78A0787}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DFC9B46A-BFA7-407D-B872-7104C78A0787}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DFC9B46A-BFA7-407D-B872-7104C78A0787}.Release|Any CPU.Build.0 = Release|Any CPU + {8C743361-B796-4A92-BD69-3B5DD734BA6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C743361-B796-4A92-BD69-3B5DD734BA6F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C743361-B796-4A92-BD69-3B5DD734BA6F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C743361-B796-4A92-BD69-3B5DD734BA6F}.Release|Any CPU.Build.0 = Release|Any CPU + {16FEFD31-B0D6-4291-B620-F902A16F39DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16FEFD31-B0D6-4291-B620-F902A16F39DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16FEFD31-B0D6-4291-B620-F902A16F39DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16FEFD31-B0D6-4291-B620-F902A16F39DC}.Release|Any CPU.Build.0 = Release|Any CPU + {573C617F-6BB2-403A-AD87-E00A7FD537F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {573C617F-6BB2-403A-AD87-E00A7FD537F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {573C617F-6BB2-403A-AD87-E00A7FD537F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {573C617F-6BB2-403A-AD87-E00A7FD537F0}.Release|Any CPU.Build.0 = Release|Any CPU + {F422398C-72CD-43EA-AC8E-E0DBD08E5563}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F422398C-72CD-43EA-AC8E-E0DBD08E5563}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F422398C-72CD-43EA-AC8E-E0DBD08E5563}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F422398C-72CD-43EA-AC8E-E0DBD08E5563}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -503,6 +573,17 @@ Global {7E9D98E7-733C-4D6B-A5AC-087D588A40ED} = {CB0CC552-2017-40C0-934A-C8A3B00EF650} {8A41B37E-0732-4F28-B214-A44233B447FE} = {92F8E9A2-903E-4025-99BC-7DC478D5466D} {BCB42780-C559-40B6-8C4A-85EBC464AAA8} = {FF69998C-C661-4EF0-804B-845675B3602E} + {A7D0995D-0516-4975-ABBD-EB93E1B79292} = {C9F82701-0E0F-4E61-B05B-AE387E7631F6} + {9164E0BA-0846-4839-BA0F-C25F5FBE056C} = {6AEE1440-FDF0-4729-8196-B24D0E333550} + {29B9F157-3733-471E-A11E-A5FF3C6D1348} = {6AEE1440-FDF0-4729-8196-B24D0E333550} + {60763BAA-C963-4858-8DA1-78DB92428865} = {6AEE1440-FDF0-4729-8196-B24D0E333550} + {1B30B69C-A4E3-4660-9CA8-140D0C34B4A5} = {6AEE1440-FDF0-4729-8196-B24D0E333550} + {BCA498E6-22C7-4E3F-8862-A7FAA06652D1} = {6AEE1440-FDF0-4729-8196-B24D0E333550} + {DFC9B46A-BFA7-407D-B872-7104C78A0787} = {6AEE1440-FDF0-4729-8196-B24D0E333550} + {8C743361-B796-4A92-BD69-3B5DD734BA6F} = {6AEE1440-FDF0-4729-8196-B24D0E333550} + {16FEFD31-B0D6-4291-B620-F902A16F39DC} = {BB874DF1-44FE-415A-B634-A6B829107890} + {573C617F-6BB2-403A-AD87-E00A7FD537F0} = {BB874DF1-44FE-415A-B634-A6B829107890} + {F422398C-72CD-43EA-AC8E-E0DBD08E5563} = {BB874DF1-44FE-415A-B634-A6B829107890} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31E0F4D5-975A-41CC-933E-545B2201FAF9} diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index 1580f7e745..2f60ab3f70 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -33,6 +33,14 @@ parameters: displayName: "Produce RTM version?" type: boolean default: False +- name: products + displayName: "Select the products to generate" + type: string + default: all + values: + - all + - mstest + - testing-platform # This option should be used with caution. This is useful for unblocking circular deps issue with testanywhere - name: SkipTests displayName: "Skip tests" @@ -54,6 +62,8 @@ variables: value: '' - name: _BuildConfig value: Release + - name: _Products + value: ${{parameters.products}} - ${{ if eq(parameters.isRTM, True) }}: - name: _ReleaseVersionKind @@ -74,6 +84,7 @@ variables: /p:DotNetSymbolServerTokenMsdl=$(microsoft-symbol-server-pat) /p:DotNetSymbolServerTokenSymWeb=$(symweb-symbol-server-pat) /p:OfficialBuildId=$(BUILD.BUILDNUMBER) + /p:ProductsToBuild=$(_Products) resources: repositories: @@ -124,6 +135,15 @@ extends: failOnStderr: true showWarnings: true + - task: PowerShell@2 + displayName: 'Install procdump' + inputs: + targetType: filePath + filePath: ./eng/install-procdump.ps1 + failOnStderr: true + showWarnings: true + + - script: eng\common\CIBuild.cmd -configuration $(_BuildConfig) -prepareMachine @@ -134,9 +154,11 @@ extends: - ${{ if eq(parameters.SkipTests, False) }}: # -ci is allowing to import some environment variables and some required configurations + # -nobl avoids overwriting build binlog with binlog from tests - script: Test.cmd -configuration $(_BuildConfig) -ci + -nobl name: Test displayName: Test @@ -181,9 +203,10 @@ extends: - ${{ if eq(parameters.SkipTests, False) }}: # -ci is allowing to import some environment variables and some required configurations + # --nobl avoids overwriting build binlog with binlog from tests - script: | chmod +x ./test.sh - ./test.sh --configuration $(_BuildConfig) --ci --test --integrationTest + ./test.sh --configuration $(_BuildConfig) --ci --test --integrationTest --nobl name: Test displayName: Tests diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 35ab1a58b2..ce6cec7a1f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -93,19 +93,30 @@ stages: failOnStderr: true showWarnings: true + - task: PowerShell@2 + displayName: 'Install procdump' + inputs: + targetType: filePath + filePath: ./eng/install-procdump.ps1 + failOnStderr: true + showWarnings: true + - script: eng\common\CIBuild.cmd -configuration $(_BuildConfig) -prepareMachine $(_InternalBuildArgs) /p:Test=false + /p:FastAcceptanceTest=true name: Build displayName: Build - ${{ if eq(parameters.SkipTests, False) }}: # -ci is allowing to import some environment variables and some required configurations + # -nobl avoids overwriting build binlog with binlog from tests - script: Test.cmd -configuration $(_BuildConfig) -ci + -nobl name: Test displayName: Test @@ -122,7 +133,7 @@ stages: timeoutInMinutes: 90 pool: name: NetCore-Public - demands: ImageOverride -equals build.ubuntu.2004.amd64.open + demands: ImageOverride -equals build.ubuntu.2004.amd64.open strategy: matrix: Release: @@ -135,12 +146,14 @@ stages: -prepareMachine /p:Test=false /p:NonWindowsBuild=true + /p:FastAcceptanceTest=true displayName: Build - ${{ if eq(parameters.SkipTests, False) }}: - # -ci is allowing to import some environment variables and some required configurations + # --ci is allowing to import some environment variables and some required configurations + # --nobl avoids overwriting build binlog with binlog from tests - script: | chmod +x ./test.sh - ./test.sh --configuration $(_BuildConfig) --ci --test --integrationTest + ./test.sh --configuration $(_BuildConfig) --ci --test --integrationTest --nobl name: Test displayName: Tests diff --git a/docs/AddingAnalyzerCodeFix.md b/docs/AddingAnalyzerCodeFix.md new file mode 100644 index 0000000000..0bc44fab9a --- /dev/null +++ b/docs/AddingAnalyzerCodeFix.md @@ -0,0 +1,13 @@ +# Adding analyzer code fix + +You should add it under src/Analyzers/MSTest.Analyzers.CodeFixes. + +Add your fixer logic and match the analyzer rule id with your analyzer. + +## To update unit tests you should replace + +`Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>;` by `MSTest.Analyzers."fixerName">;` + +`VerifyCS.VerifyAnalyzerAsync` by `VerifyCS.VerifyCodeFixAsync` + +you can use this PR as refrence:[https://github.com/microsoft/testfx/pull/3091] diff --git a/docs/Changelog.md b/docs/Changelog.md index 83ec291252..6b484a34b9 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -2,11 +2,76 @@ All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) -## [3.4.0] - WIP +## [3.4.3] - 2024-05-30 -See full log [here](https://github.com/microsoft/testfx/compare/v3.3.1...HEAD) +See full log [here](https://github.com/microsoft/testfx/compare/v3.4.2...v3.4.3) + +### Fixed + +* Revert version of Code Coverage to 17.10.4 by @Evangelink in [#3048](https://github.com/microsoft/testfx/pull/3048) + +### Artifacts + +* MSTest: [3.4.3](https://www.nuget.org/packages/MSTest/3.4.3) +* MSTest.TestFramework: [3.4.3](https://www.nuget.org/packages/MSTest.TestFramework/3.4.3) +* MSTest.TestAdapter: [3.4.3](https://www.nuget.org/packages/MSTest.TestAdapter/3.4.3) +* MSTest.Analyzers: [3.4.3](https://www.nuget.org/packages/MSTest.Analyzers/3.4.3) +* MSTest.Sdk: [3.4.3](https://www.nuget.org/packages/MSTest.Sdk/3.4.3) +* Microsoft.Testing.Extensions.CrashDump: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.2.1) +* Microsoft.Testing.Extensions.HangDump: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.2.1) +* Microsoft.Testing.Extensions.HotReload: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.2.1) +* Microsoft.Testing.Extensions.Retry: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.2.1) +* Microsoft.Testing.Extensions.TrxReport: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.2.1) + +## [3.4.2] - 2024-05-30 + +See full log [here](https://github.com/microsoft/testfx/compare/v3.4.1...v3.4.2) + +### Fixed + +* Use latest released version for Playwright and Aspire by @Evangelink in [#3024](https://github.com/microsoft/testfx/pull/3024) +* Fix project samples for 3.4 by @Evangelink in [#3032](https://github.com/microsoft/testfx/pull/3032) +* Fix assembly resolution with DeploymentItem by @Evangelink in [#3034](https://github.com/microsoft/testfx/pull/3034) + +### Artifacts + +* MSTest: [3.4.2](https://www.nuget.org/packages/MSTest/3.4.2) +* MSTest.TestFramework: [3.4.2](https://www.nuget.org/packages/MSTest.TestFramework/3.4.2) +* MSTest.TestAdapter: [3.4.2](https://www.nuget.org/packages/MSTest.TestAdapter/3.4.2) +* MSTest.Analyzers: [3.4.2](https://www.nuget.org/packages/MSTest.Analyzers/3.4.2) +* MSTest.Sdk: [3.4.2](https://www.nuget.org/packages/MSTest.Sdk/3.4.2) +* Microsoft.Testing.Extensions.CrashDump: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.2.1) +* Microsoft.Testing.Extensions.HangDump: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.2.1) +* Microsoft.Testing.Extensions.HotReload: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.2.1) +* Microsoft.Testing.Extensions.Retry: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.2.1) +* Microsoft.Testing.Extensions.TrxReport: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.2.1) + +## [3.4.1] - 2024-05-27 + +See full log [here](https://github.com/microsoft/testfx/compare/v3.4.0...v3.4.1) + +### Fixed + +* Fix assembly resolution error by @Evangelink in [#2948](https://github.com/microsoft/testfx/pull/2948) + +### Artifacts + +* MSTest: [3.4.1](https://www.nuget.org/packages/MSTest/3.4.1) +* MSTest.TestFramework: [3.4.1](https://www.nuget.org/packages/MSTest.TestFramework/3.4.1) +* MSTest.TestAdapter: [3.4.1](https://www.nuget.org/packages/MSTest.TestAdapter/3.4.1) +* MSTest.Analyzers: [3.4.1](https://www.nuget.org/packages/MSTest.Analyzers/3.4.1) +* MSTest.Sdk: [3.4.1](https://www.nuget.org/packages/MSTest.Sdk/3.4.1) +* Microsoft.Testing.Extensions.CrashDump: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.2.1) +* Microsoft.Testing.Extensions.HangDump: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.2.1) +* Microsoft.Testing.Extensions.HotReload: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.2.1) +* Microsoft.Testing.Extensions.Retry: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.2.1) +* Microsoft.Testing.Extensions.TrxReport: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.2.1) + +## [3.4.0] - 2024-05-23 + +See full log [here](https://github.com/microsoft/testfx/compare/v3.3.1...v3.4.0) ### Added @@ -57,6 +122,10 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.3.1...HEAD) * Fix localappdata folder for Linux and Mac by @Evangelink in [#2765](https://github.com/microsoft/testfx/pull/2765) * Fix deserializers for DiscoveryRequestArgs and RunRequestArgs by @mariam-abdulla in [#2768](https://github.com/microsoft/testfx/pull/2768) * Don't start thread/task when not using timeout for fixture methods by @Evangelink in [#2825](https://github.com/microsoft/testfx/pull/2825) +* Fix parameters/arguments check for data driven tests by @nohwnd in [#2829](https://github.com/microsoft/testfx/pull/2829) +* Cleaning command line validations and adding unit tests by @fhnaseer in [#2847](https://github.com/microsoft/testfx/pull/2847) +* Fix MSTEST0014 FP with arrays by @Evangelink in [#2857](https://github.com/microsoft/testfx/pull/2857) +* Flow execution context across fixture methods when using timeout by @Evangelink [#2843](https://github.com/microsoft/testfx/pull/2843) ### Housekeeping @@ -71,9 +140,31 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.3.1...HEAD) * Add test for resource recursion problem by @nohwnd in [#2778](https://github.com/microsoft/testfx/pull/2778) * Opt-out from CPM in samples by @Evangelink in [#2805](https://github.com/microsoft/testfx/pull/2805) +### New Contributors + +* @mariam-abdulla made their first contribution in [#2564](https://github.com/microsoft/testfx/pull/2564) +* @Varorbc made their first contribution in [#2696](https://github.com/microsoft/testfx/pull/2696) +* @skanda890 made their first contribution in [#2706](https://github.com/microsoft/testfx/pull/2706) +* @SimonCropp made their first contribution in [#2714](https://github.com/microsoft/testfx/pull/2714) +* @Mrxx99 made their first contribution in [#2717](https://github.com/microsoft/testfx/pull/2717) +* @dansiegel made their first contribution in [#2727](https://github.com/microsoft/testfx/pull/2727) +* @thomhurst made their first contribution in [#2749](https://github.com/microsoft/testfx/pull/2749) +* @Youssef1313 made their first contribution in [#2799](https://github.com/microsoft/testfx/pull/2799) + ### Artifacts -## [3.3.1] - 2024-04-04 +* MSTest: [3.4.0](https://www.nuget.org/packages/MSTest/3.4.0) +* MSTest.TestFramework: [3.4.0](https://www.nuget.org/packages/MSTest.TestFramework/3.4.0) +* MSTest.TestAdapter: [3.4.0](https://www.nuget.org/packages/MSTest.TestAdapter/3.4.0) +* MSTest.Analyzers: [3.4.0](https://www.nuget.org/packages/MSTest.Analyzers/3.4.0) +* MSTest.Sdk: [3.4.0](https://www.nuget.org/packages/MSTest.Sdk/3.4.0) +* Microsoft.Testing.Extensions.CrashDump: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/1.2.1) +* Microsoft.Testing.Extensions.HangDump: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/1.2.1) +* Microsoft.Testing.Extensions.HotReload: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/1.2.1) +* Microsoft.Testing.Extensions.Retry: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.2.1) +* Microsoft.Testing.Extensions.TrxReport: [1.2.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.2.1) + +## [3.3.1] - 2024-04-04 See full log [here](https://github.com/microsoft/testfx/compare/v3.3.0...v3.3.1) @@ -94,7 +185,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.3.0...v3.3.1) * Microsoft.Testing.Extensions.Retry: [1.1.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.1.0) * Microsoft.Testing.Extensions.TrxReport: [1.1.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.1.0) -## [3.3.0] - 2024-04-03 +## [3.3.0] - 2024-04-03 See full log [here](https://github.com/microsoft/testfx/compare/v3.2.2...v3.3.0) @@ -167,7 +258,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.2.2...v3.3.0) * Microsoft.Testing.Extensions.Retry: [1.1.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.1.0) * Microsoft.Testing.Extensions.TrxReport: [1.1.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.1.0) -## [3.2.2] - 2024-02-22 +## [3.2.2] - 2024-02-22 See full log [here](https://github.com/microsoft/testfx/compare/v3.2.1...v3.2.2) @@ -191,7 +282,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.2.1...v3.2.2) * Microsoft.Testing.Extensions.Retry: [1.0.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.0.2) * Microsoft.Testing.Extensions.TrxReport: [1.0.2](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.0.2) -## [3.2.1] - 2024-02-13 +## [3.2.1] - 2024-02-13 See full log [here](https://github.com/microsoft/testfx/compare/v3.2.0...v.3.2.1) @@ -221,7 +312,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.2.0...v.3.2.1 * Microsoft.Testing.Extensions.Retry: [1.0.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.0.1) * Microsoft.Testing.Extensions.TrxReport: [1.0.1](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.0.1) -## [3.2.0] - 2024-01-24 +## [3.2.0] - 2024-01-24 See full log [here](https://github.com/microsoft/testfx/compare/v3.1.1...v.3.2.0) @@ -302,7 +393,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.1.1...v.3.2.0 * Microsoft.Testing.Extensions.Retry: [1.0.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.0.0) * Microsoft.Testing.Extensions.TrxReport: [1.0.0](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.0.0) -## [3.2.0-preview.24069.3] - 2024-01-19 +## [3.2.0-preview.24069.3] - 2024-01-19 See full log [here](https://github.com/microsoft/testfx/compare/v3.2.0-preview.23623.1...v3.2.0-preview.24069.3) @@ -355,7 +446,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.2.0-preview.2 * Microsoft.Testing.Extensions.Retry: [1.0.0-preview.24068.6](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/1.0.0-preview.24068.6) * Microsoft.Testing.Extensions.TrxReport: [1.0.0-preview.24068.6](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/1.0.0-preview.24068.6) -## [3.2.0-preview.23623.1] - 2023-12-23 +## [3.2.0-preview.23623.1] - 2023-12-23 See full log [here](https://github.com/Microsoft/testfx/compare/v3.2.0-preview.23622.1...3.2.0-preview.23623.1) @@ -370,7 +461,7 @@ See full log [here](https://github.com/Microsoft/testfx/compare/v3.2.0-preview.2 * MSTest.TestAdapter: [3.2.0-preview.23623.1](https://www.nuget.org/packages/MSTest.TestAdapter/3.2.0-preview.23623.1) * MSTest.Analyzers: [3.2.0-preview.23623.1](https://www.nuget.org/packages/MSTest.Analyzers/3.2.0-preview.23623.1) -## [3.2.0-preview.23622.1] - 2023-12-22 +## [3.2.0-preview.23622.1] - 2023-12-22 See full log [here](https://github.com/Microsoft/testfx/compare/v3.1.1...v3.2.0-preview.23622.1) @@ -418,7 +509,7 @@ See full log [here](https://github.com/Microsoft/testfx/compare/v3.1.1...v3.2.0- * MSTest.TestAdapter: [3.2.0-preview.23622.1](https://www.nuget.org/packages/MSTest.TestAdapter/3.2.0-preview.23622.1) * MSTest.Analyzers: [3.2.0-preview.23622.1](https://www.nuget.org/packages/MSTest.Analyzers/3.2.0-preview.23622.1) -## [3.1.1] - 2023-07-14 +## [3.1.1] - 2023-07-14 ### Fixed @@ -432,7 +523,7 @@ See full log [here](https://github.com/Microsoft/testfx/compare/v3.1.0...v3.1.1) * MSTest.TestFramework: [3.1.1](https://www.nuget.org/packages/MSTest.TestFramework/3.1.1) * MSTest.TestAdapter: [3.1.1](https://www.nuget.org/packages/MSTest.TestAdapter/3.1.1) -## [3.1.0] - 2023-07-14 +## [3.1.0] - 2023-07-14 See full log [here](https://github.com/Microsoft/testfx/compare/v3.0.4...v3.1.0) @@ -478,7 +569,7 @@ See full log [here](https://github.com/Microsoft/testfx/compare/v3.0.4...v3.1.0) * MSTest.TestFramework: [3.1.0](https://www.nuget.org/packages/MSTest.TestFramework/3.1.0) * MSTest.TestAdapter: [3.1.0](https://www.nuget.org/packages/MSTest.TestAdapter/3.1.0) -## [3.0.4] - 2023-06-01 +## [3.0.4] - 2023-06-01 See full log [here](https://github.com/microsoft/testfx/compare/v3.0.3...v3.0.4) @@ -493,7 +584,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.0.3...v3.0.4) * MSTest.TestFramework: [3.0.4](https://www.nuget.org/packages/MSTest.TestFramework/3.0.4) * MSTest.TestAdapter: [3.0.4](https://www.nuget.org/packages/MSTest.TestAdapter/3.0.4) -## [3.0.3] - 2023-05-24 +## [3.0.3] - 2023-05-24 See full log [here](https://github.com/Microsoft/testfx/compare/v3.0.2...v3.0.3) @@ -514,7 +605,7 @@ See full log [here](https://github.com/Microsoft/testfx/compare/v3.0.2...v3.0.3) * MSTest.TestFramework: [3.0.3](https://www.nuget.org/packages/MSTest.TestFramework/3.0.3) * MSTest.TestAdapter: [3.0.3](https://www.nuget.org/packages/MSTest.TestAdapter/3.0.3) -## [3.0.2] - 2022-12-27 +## [3.0.2] - 2022-12-27 See full log [here](https://github.com/microsoft/testfx/compare/v3.0.1...v3.0.2) @@ -528,7 +619,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.0.1...v3.0.2) * MSTest.TestFramework: [3.0.2](https://www.nuget.org/packages/MSTest.TestFramework/3.0.2) * MSTest.TestAdapter: [3.0.2](https://www.nuget.org/packages/MSTest.TestAdapter/3.0.2) -## [3.0.1] - 2022-12-20 +## [3.0.1] - 2022-12-20 See full log [here](https://github.com/microsoft/testfx/compare/v3.0.0...v3.0.1) @@ -551,7 +642,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.0.0...v3.0.1) * MSTest.TestFramework: [3.0.1](https://www.nuget.org/packages/MSTest.TestFramework/3.0.1) * MSTest.TestAdapter: [3.0.1](https://www.nuget.org/packages/MSTest.TestAdapter/3.0.1) -## [3.0.0] - 2022-12-06 +## [3.0.0] - 2022-12-06 See full log [here](https://github.com/microsoft/testfx/compare/v2.2.10...v3.0.0) @@ -608,7 +699,7 @@ Breaking changes announcements [#1274](https://github.com/microsoft/testfx/issue * MSTest.TestFramework: [3.0.0](https://www.nuget.org/packages/MSTest.TestFramework/3.0.0) * MSTest.TestAdapter: [3.0.0](https://www.nuget.org/packages/MSTest.TestAdapter/3.0.0) -## [3.0.0-preview-20221122-01] - 2022-11-23 +## [3.0.0-preview-20221122-01] - 2022-11-23 See full log [here](https://github.com/microsoft/testfx/compare/v3.0.0-preview-20221110-04...v3.0.0-preview-20221122-01) @@ -652,7 +743,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v3.0.0-preview-2 * MSTest.TestFramework: [3.0.0-preview-20221122-01](https://www.nuget.org/packages/MSTest.TestFramework/3.0.0-preview-20221122-01) * MSTest.TestAdapter: [3.0.0-preview-20221122-01](https://www.nuget.org/packages/MSTest.TestAdapter/3.0.0-preview-20221122-01) -## [3.0.0-preview-20221110-04] - 2022-11-11 +## [3.0.0-preview-20221110-04] - 2022-11-11 See full log [here](https://github.com/microsoft/testfx/compare/v2.3.0-preview-20220810-02...v3.0.0-preview-20221110-04) @@ -785,7 +876,7 @@ See full log [here](https://github.com/microsoft/testfx/compare/v2.3.0-preview-2 * MSTest.TestFramework: [3.0.0-preview-20221110-04](https://www.nuget.org/packages/MSTest.TestFramework/3.0.0-preview-20221110-04) * MSTest.TestAdapter: [3.0.0-preview-20221110-04](https://www.nuget.org/packages/MSTest.TestAdapter/3.0.0-preview-20221110-04) -## [2.3.0-preview-20220810-02] 2022-08-10 +## [2.3.0-preview-20220810-02] 2022-08-10 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.2.10...v2.3.0-preview-20220810-02) @@ -818,7 +909,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.3.0-preview-20220810-02](https://www.nuget.org/packages/MSTest.TestFramework/2.3.0-preview-20220810-02) * MSTest.TestAdapter: [2.3.0-preview-20220810-02](https://www.nuget.org/packages/MSTest.TestAdapter/2.3.0-preview-20220810-02) -## [2.2.10] - 2022-04-26 +## [2.2.10] - 2022-04-26 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.2.10-preview-20220414-01...v2.2.10) @@ -845,7 +936,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.10](https://www.nuget.org/packages/MSTest.TestFramework/2.2.10) * MSTest.TestAdapter: [2.2.10](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.10) -## [2.2.10-preview-20220414-01] - 2022-04-14 +## [2.2.10-preview-20220414-01] - 2022-04-14 ### Fixed @@ -858,7 +949,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.10-preview-20220414-01](https://www.nuget.org/packages/MSTest.TestFramework/2.2.10-preview-20220414-01) * MSTest.TestAdapter: [2.2.10-preview-20220414-01](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.10-preview-20220414-01) -## [2.2.9] 2022-04-08 +## [2.2.9] 2022-04-08 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.2.8...v2.2.9) @@ -892,7 +983,7 @@ Due to the way that class and assembly initialize, and cleanup are invoked, thei * MSTest.TestFramework: [2.2.9](https://www.nuget.org/packages/MSTest.TestFramework/2.2.9) * MSTest.TestAdapter: [2.2.9](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.9) -## [2.2.8] - 2021-11-23 +## [2.2.8] - 2021-11-23 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.2.7...v2.2.8) @@ -924,7 +1015,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.8](https://www.nuget.org/packages/MSTest.TestFramework/2.2.8) * MSTest.TestAdapter: [2.2.8](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.8) -## [2.2.7] - 2021-09-03 +## [2.2.7] - 2021-09-03 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.2.6...v2.2.7) @@ -941,7 +1032,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.7](https://www.nuget.org/packages/MSTest.TestFramework/2.2.7) * MSTest.TestAdapter: [2.2.7](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.7) -## [2.2.6] - 2021-08-25 +## [2.2.6] - 2021-08-25 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.2.5...v2.2.6) @@ -959,7 +1050,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.6](https://www.nuget.org/packages/MSTest.TestFramework/2.2.6) * MSTest.TestAdapter: [2.2.6](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.6) -## [2.2.5] - 2021-06-28 +## [2.2.5] - 2021-06-28 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.2.4...v2.2.5) @@ -984,7 +1075,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.5](https://www.nuget.org/packages/MSTest.TestFramework/2.2.5) * MSTest.TestAdapter: [2.2.5](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.5) -## [2.2.4] - 2021-05-25 +## [2.2.4] - 2021-05-25 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/0b95a26282eae17f896d732381e5c77b9a603382...v2.2.4) @@ -993,7 +1084,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.4](https://www.nuget.org/packages/MSTest.TestFramework/2.2.4) * MSTest.TestAdapter: [2.2.4](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.4) -## [2.2.4-preview-20210331-02] - 2021-04-02 +## [2.2.4-preview-20210331-02] - 2021-04-02 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.2.3...v2.2.4-preview-20210331-02) @@ -1015,7 +1106,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.4-preview-20210331-02](https://www.nuget.org/packages/MSTest.TestFramework/2.2.4-preview-20210331-02) * MSTest.TestAdapter: [2.2.4-preview-20210331-02](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.4-preview-20210331-02) -## [2.2.3] - 2021-03-16 +## [2.2.3] - 2021-03-16 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.2.2...v2.2.3) @@ -1028,7 +1119,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.3](https://www.nuget.org/packages/MSTest.TestFramework/2.2.3) * MSTest.TestAdapter: [2.2.3](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.3) -## [2.2.2] - 2021-03-15 +## [2.2.2] - 2021-03-15 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.2.1...v2.2.2) @@ -1047,7 +1138,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.2](https://www.nuget.org/packages/MSTest.TestFramework/2.2.2) * MSTest.TestAdapter: [2.2.2](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.2) -## [2.2.1] - 2021-03-01 +## [2.2.1] - 2021-03-01 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.2.0-preview-20210115-03...v2.2.1) @@ -1076,7 +1167,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.1](https://www.nuget.org/packages/MSTest.TestFramework/2.2.1) * MSTest.TestAdapter: [2.2.1](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.1) -## [2.2.0-preview-20210115-03] - 2021-01-20 +## [2.2.0-preview-20210115-03] - 2021-01-20 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.2.0-preview-20201126-03...v2.2.0-preview-20210115-03) @@ -1103,7 +1194,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.0-preview-20210115-03](https://www.nuget.org/packages/MSTest.TestFramework/2.2.0-preview-20210115-03) * MSTest.TestAdapter: [2.2.0-preview-20210115-03](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.0-preview-20210115-03) -## [2.2.0-preview-20201126-03] - 2020-11-26 +## [2.2.0-preview-20201126-03] - 2020-11-26 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.1.2...v2.2.0-preview-20201126-03) @@ -1133,7 +1224,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.2.0-preview-20201126-03](https://www.nuget.org/packages/MSTest.TestFramework/2.2.0-preview-20201126-03) * MSTest.TestAdapter: [2.2.0-preview-20201126-03](https://www.nuget.org/packages/MSTest.TestAdapter/2.2.0-preview-20201126-03) -## [2.1.2] - 2020-06-08 +## [2.1.2] - 2020-06-08 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.1.1...v2.1.2) @@ -1154,7 +1245,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.1.2](https://www.nuget.org/packages/MSTest.TestFramework/2.1.2) * MSTest.TestAdapter: [2.1.2](https://www.nuget.org/packages/MSTest.TestAdapter/2.1.2) -## [2.1.1] - 2020-04-01 +## [2.1.1] - 2020-04-01 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.1.0...v2.1.1) @@ -1181,7 +1272,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.1.1](https://www.nuget.org/packages/MSTest.TestFramework/2.1.1) * MSTest.TestAdapter: [2.1.1](https://www.nuget.org/packages/MSTest.TestAdapter/2.1.1) -## [2.1.0] - 2020-02-03 +## [2.1.0] - 2020-02-03 A list of changes since last release are available [here](https://github.com/microsoft/testfx/compare/v2.1.0-beta2...v2.1.0) @@ -1199,7 +1290,7 @@ A list of changes since last release are available [here](https://github.com/mic * MSTest.TestFramework: [2.1.0](https://www.nuget.org/packages/MSTest.TestFramework/2.1.0) * MSTest.TestAdapter: [2.1.0](https://www.nuget.org/packages/MSTest.TestAdapter/2.1.0) -## [2.1.0-beta2] - 2019-12-18 +## [2.1.0-beta2] - 2019-12-18 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v2.1.0-beta...v2.1.0-beta2) @@ -1212,7 +1303,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [2.1.0-beta2](https://www.nuget.org/packages/MSTest.TestFramework/2.1.0-beta2) * MSTest.TestAdapter: [2.1.0-beta2](https://www.nuget.org/packages/MSTest.TestAdapter/2.1.0-beta2) -## [2.1.0-beta] - 2019-11-28 +## [2.1.0-beta] - 2019-11-28 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v2.0.0...v2.1.0-beta) @@ -1226,7 +1317,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [2.1.0-beta](https://www.nuget.org/packages/MSTest.TestFramework/2.1.0-beta) * MSTest.TestAdapter: [2.1.0-beta](https://www.nuget.org/packages/MSTest.TestAdapter/2.1.0-beta) -## [2.0.0] 2019-09-03 +## [2.0.0] 2019-09-03 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v2.0.0-beta4...v2.0.0) @@ -1250,7 +1341,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [2.0.0](https://www.nuget.org/packages/MSTest.TestFramework/2.0.0) * MSTest.TestAdapter: [2.0.0](https://www.nuget.org/packages/MSTest.TestAdapter/2.0.0) -## [2.0.0-beta4] - 2019-04-10 +## [2.0.0-beta4] - 2019-04-10 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/2.0.0-beta2...v2.0.0-beta4) @@ -1265,7 +1356,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [2.0.0-beta4](https://www.nuget.org/packages/MSTest.TestFramework/2.0.0-beta4) * MSTest.TestAdapter: [2.0.0-beta4](https://www.nuget.org/packages/MSTest.TestAdapter/2.0.0-beta4) -## [2.0.0-beta2] - 2019-02-15 +## [2.0.0-beta2] - 2019-02-15 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/1.4.0...2.0.0-beta2) @@ -1280,7 +1371,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [2.0.0-beta2](https://www.nuget.org/packages/MSTest.TestFramework/2.0.0-beta2) * MSTest.TestAdapter: [2.0.0-beta2](https://www.nuget.org/packages/MSTest.TestAdapter/2.0.0-beta2) -## [1.4.0] - 2018-11-26 +## [1.4.0] - 2018-11-26 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/1.4.0-beta...1.4.0) @@ -1302,7 +1393,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.4.0](https://www.nuget.org/packages/MSTest.TestFramework/1.4.0) * MSTest.TestAdapter: [1.4.0](https://www.nuget.org/packages/MSTest.TestAdapter/1.4.0) -## [1.4.0-beta] 2018-10-17 +## [1.4.0-beta] 2018-10-17 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/1.3.2...1.4.0-beta) @@ -1320,7 +1411,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.4.0-beta](https://www.nuget.org/packages/MSTest.TestFramework/1.4.0-beta) * MSTest.TestAdapter: [1.4.0-beta](https://www.nuget.org/packages/MSTest.TestAdapter/1.4.0-beta) -## [1.3.2] - 2018-06-06 +## [1.3.2] - 2018-06-06 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v1.3.1...v1.3.2) @@ -1333,7 +1424,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.3.2](https://www.nuget.org/packages/MSTest.TestFramework/1.3.2) * MSTest.TestAdapter: [1.3.2](https://www.nuget.org/packages/MSTest.TestAdapter/1.3.2) -## [1.3.1] - 2018-05-25 +## [1.3.1] - 2018-05-25 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v1.3.0...v1.3.1) @@ -1347,7 +1438,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.3.1](https://www.nuget.org/packages/MSTest.TestFramework/1.3.1) * MSTest.TestAdapter: [1.3.1](https://www.nuget.org/packages/MSTest.TestAdapter/1.3.1) -## [1.3.0] - 2018-05-11 +## [1.3.0] - 2018-05-11 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v1.2.1...v1.3.0) @@ -1369,7 +1460,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.3.0](https://www.nuget.org/packages/MSTest.TestFramework/1.3.0) * MSTest.TestAdapter: [1.3.0](https://www.nuget.org/packages/MSTest.TestAdapter/1.3.0) -## [1.3.0-beta2] - 2018-01-15 +## [1.3.0-beta2] - 2018-01-15 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v1.2.0...v1.3.0-beta2) @@ -1395,7 +1486,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.3.0-beta2](https://www.nuget.org/packages/MSTest.TestFramework/1.3.0-beta2) * MSTest.TestAdapter: [1.3.0-beta2](https://www.nuget.org/packages/MSTest.TestAdapter/1.3.0-beta2) -## [1.2.1] - 2018-04-05 +## [1.2.1] - 2018-04-05 ### Changed @@ -1412,7 +1503,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.2.1](https://www.nuget.org/packages/MSTest.TestFramework/1.2.1) * MSTest.TestAdapter: [1.2.1](https://www.nuget.org/packages/MSTest.TestAdapter/1.2.1) -## [1.2.0] - 2017-10-11 +## [1.2.0] - 2017-10-11 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v1.2.0-beta3...v1.2.0) @@ -1432,7 +1523,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.2.0](https://www.nuget.org/packages/MSTest.TestFramework/1.2.0) * MSTest.TestAdapter: [1.2.0](https://www.nuget.org/packages/MSTest.TestAdapter/1.2.0) -## [1.2.0-beta3] - 2017-08-09 +## [1.2.0-beta3] - 2017-08-09 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v1.2.0-beta...v1.2.0-beta3) @@ -1452,7 +1543,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.2.0-beta3](https://www.nuget.org/packages/MSTest.TestFramework/1.2.0-beta3) * MSTest.TestAdapter: [1.2.0-beta3](https://www.nuget.org/packages/MSTest.TestAdapter/1.2.0-beta3) -## [1.2.0-beta] - 2017-06-29 +## [1.2.0-beta] - 2017-06-29 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v1.1.18...v1.2.0-beta) @@ -1467,7 +1558,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.2.0-beta](https://www.nuget.org/packages/MSTest.TestFramework/1.2.0-beta) * MSTest.TestAdapter: [1.2.0-beta](https://www.nuget.org/packages/MSTest.TestAdapter/1.2.0-beta) -## [1.1.18] - 2017-06-01 +## [1.1.18] - 2017-06-01 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v1.1.17...v1.1.18) @@ -1485,7 +1576,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.1.18](https://www.nuget.org/packages/MSTest.TestFramework/1.1.18) * MSTest.TestAdapter: [1.1.18](https://www.nuget.org/packages/MSTest.TestAdapter/1.1.18) -## [1.1.17] - 2017-04-21 +## [1.1.17] - 2017-04-21 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v1.1.14...v1.1.17) @@ -1503,7 +1594,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.1.17](https://www.nuget.org/packages/MSTest.TestFramework/1.1.17) * MSTest.TestAdapter: [1.1.17](https://www.nuget.org/packages/MSTest.TestAdapter/1.1.17) -## [1.1.14] - 2017-03-31 +## [1.1.14] - 2017-03-31 A list of changes since last release are available [here](https://github.com/Microsoft/testfx/compare/v1.1.13...v1.1.14) @@ -1520,7 +1611,7 @@ A list of changes since last release are available [here](https://github.com/Mic * MSTest.TestFramework: [1.1.14](https://www.nuget.org/packages/MSTest.TestFramework/1.1.14) * MSTest.TestAdapter: [1.1.14](https://www.nuget.org/packages/MSTest.TestAdapter/1.1.14) -## [1.1.13] - 2017-03-10 +## [1.1.13] - 2017-03-10 This is also the first release from GitHub and with source code building against Dev15 tooling. @@ -1535,7 +1626,7 @@ This is also the first release from GitHub and with source code building against * MSTest.TestFramework: [1.1.13](https://www.nuget.org/packages/MSTest.TestFramework/1.1.13) * MSTest.TestAdapter: [1.1.13](https://www.nuget.org/packages/MSTest.TestAdapter/1.1.13) -## [1.1.11] - 2017-02-17 +## [1.1.11] - 2017-02-17 Initial release. diff --git a/docs/testingplatform/Index.md b/docs/testingplatform/Index.md index 2714611072..34e78d2c84 100644 --- a/docs/testingplatform/Index.md +++ b/docs/testingplatform/Index.md @@ -10,6 +10,8 @@ 1. [Available requests](irequest.md) 1. [Well known TestNodeUpdateMessage.TestNode properties](testnodeupdatemessage.md) 1. [Capabilities](capabilities.md) + 1. [ITestFrameworkCapability](itestframeworkcapability.md) + 1. [IBannerMessageOwnerCapability](ibannermessageownercapability.md) 1. Extensions 1. [Introduction](extensionintro.md) 1. In-process & out-of-process @@ -18,9 +20,13 @@ 1. [ITestSessionLifetimeHandler](itestsessionlifetimehandler.md) 1. [ITestApplicationLifecycleCallbacks](itestapplicationlifecyclecallbacks.md) 1. [IDataConsumer](idataconsumer.md) + 1. Out-of-process + 1. [ITestHostEnvironmentVariableProvider](itesthostenvironmentvariableprovider.md) + 1. [ITestHostProcessLifetimeHandler](itesthostprocesslifetimehandler.md) 1. Extensions miscellaneous 1. [IAsyncInitializableExtension & IAsyncCleanableExtension](asyncinitcleanup.md) 1. [CompositeExtensionFactory\](compositeextensionfactory.md) +1. [Testing framework & extensions execution order](executionorder.md) 1. Services 1. [IServiceProvider](iserviceprovider.md) 1. [IConfiguration](configuration.md) @@ -28,4 +34,5 @@ 1. [IMessageBus](imessagebus.md) 1. [ICommandLineOptions](icommandlineoptions.md) 1. [IOutputDevice](ioutputdevice.md) + 1. [IPlatformInformation](iplatforminformation.md) 1. [Code sample](codesample.md) diff --git a/docs/testingplatform/Source/TestingPlatformExplorer/Out-of-process extensions/MonitorTestHost.cs b/docs/testingplatform/Source/TestingPlatformExplorer/Out-of-process extensions/MonitorTestHost.cs new file mode 100644 index 0000000000..a685152567 --- /dev/null +++ b/docs/testingplatform/Source/TestingPlatformExplorer/Out-of-process extensions/MonitorTestHost.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestHostControllers; +using Microsoft.Testing.Platform.OutputDevice; + +namespace TestingPlatformExplorer.OutOfProcess; + +internal class MonitorTestHost : ITestHostProcessLifetimeHandler, IOutputDeviceDataProducer +{ + private readonly IOutputDevice _outputDevice; + + public MonitorTestHost(IOutputDevice outputDevice) + { + _outputDevice = outputDevice; + } + + public string Uid => nameof(MonitorTestHost); + + public string Version => "1.0.0"; + + public string DisplayName => nameof(MonitorTestHost); + + public string Description => "Example of monitoring the test host process."; + + public Task IsEnabledAsync() => Task.FromResult(true); + + public async Task BeforeTestHostProcessStartAsync(CancellationToken cancellationToken) + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData("BeforeTestHostProcessStartAsync") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } + }); + + public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testHostProcessInformation, CancellationToken cancellation) + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"OnTestHostProcessExitedAsync, test host exited with exit code {testHostProcessInformation.ExitCode}") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } + }); + + public async Task OnTestHostProcessStartedAsync(ITestHostProcessInformation testHostProcessInformation, CancellationToken cancellation) + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"OnTestHostProcessStartedAsync, test host started with PID {testHostProcessInformation.PID}") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } + }); +} diff --git a/docs/testingplatform/Source/TestingPlatformExplorer/Out-of-process extensions/SetEnvironmentVariableForTestHost.cs b/docs/testingplatform/Source/TestingPlatformExplorer/Out-of-process extensions/SetEnvironmentVariableForTestHost.cs new file mode 100644 index 0000000000..b3b0fb7160 --- /dev/null +++ b/docs/testingplatform/Source/TestingPlatformExplorer/Out-of-process extensions/SetEnvironmentVariableForTestHost.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.TestHostControllers; + +namespace TestingPlatformExplorer.OutOfProcess; + +internal class SetEnvironmentVariableForTestHost : ITestHostEnvironmentVariableProvider +{ + public string Uid => nameof(SetEnvironmentVariableForTestHost); + + public string Version => "1.0.0"; + + public string DisplayName => nameof(SetEnvironmentVariableForTestHost); + + public string Description => "Example of setting environment variables for the test host."; + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task UpdateAsync(IEnvironmentVariables environmentVariables) + { + environmentVariables.SetVariable(new EnvironmentVariable("SAMPLE", "SAMPLE_VALUE", false, true)); + return Task.CompletedTask; + } + + public Task ValidateTestHostEnvironmentVariablesAsync(IReadOnlyEnvironmentVariables environmentVariables) + => environmentVariables.TryGetVariable("SAMPLE", out OwnedEnvironmentVariable? value) && value.Value == "SAMPLE_VALUE" + ? ValidationResult.ValidTask + : ValidationResult.InvalidTask("The environment variable 'SAMPLE' is not set to 'SAMPLE_VALUE'."); +} diff --git a/docs/testingplatform/Source/TestingPlatformExplorer/Program.cs b/docs/testingplatform/Source/TestingPlatformExplorer/Program.cs index 210c037ae2..441c6d7cfd 100644 --- a/docs/testingplatform/Source/TestingPlatformExplorer/Program.cs +++ b/docs/testingplatform/Source/TestingPlatformExplorer/Program.cs @@ -8,13 +8,14 @@ using Microsoft.Testing.Platform.Services; using TestingPlatformExplorer.InProcess; +using TestingPlatformExplorer.OutOfProcess; using TestingPlatformExplorer.TestingFramework; // Create the test application builder ITestApplicationBuilder testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); // Register the testing framework -testApplicationBuilder.AddTestingFramework(new[] { Assembly.GetExecutingAssembly() }); +testApplicationBuilder.AddTestingFramework(() => new[] { Assembly.GetExecutingAssembly() }); // In-process & out-of-process extensions // Register the testing framework command line options @@ -28,6 +29,12 @@ testApplicationBuilder.TestHost.AddDataConsumer(serviceProvider => new DisplayDataConsumer(serviceProvider.GetOutputDevice())); +// Out-of-process extensions +testApplicationBuilder.TestHostControllers.AddEnvironmentVariableProvider(_ + => new SetEnvironmentVariableForTestHost()); +testApplicationBuilder.TestHostControllers.AddProcessLifetimeHandler(serviceProvider => + new MonitorTestHost(serviceProvider.GetOutputDevice())); + // In-process composite extension SessionLifeTimeHandler+DataConsumer CompositeExtensionFactory compositeExtensionFactory = new(serviceProvider => new DisplayCompositeExtensionFactorySample(serviceProvider.GetOutputDevice())); testApplicationBuilder.TestHost.AddTestSessionLifetimeHandle(compositeExtensionFactory); diff --git a/docs/testingplatform/Source/TestingPlatformExplorer/TestingFramework/TestingFramework.Registration.cs b/docs/testingplatform/Source/TestingPlatformExplorer/TestingFramework/TestingFramework.Registration.cs index 46535dc94e..5d2c2ddb9a 100644 --- a/docs/testingplatform/Source/TestingPlatformExplorer/TestingFramework/TestingFramework.Registration.cs +++ b/docs/testingplatform/Source/TestingPlatformExplorer/TestingFramework/TestingFramework.Registration.cs @@ -10,7 +10,7 @@ namespace TestingPlatformExplorer.TestingFramework; public static class TestingFrameworkExtensions { - public static void AddTestingFramework(this ITestApplicationBuilder builder, Assembly[] assemblies) + public static void AddTestingFramework(this ITestApplicationBuilder builder, Func assemblies) => builder.RegisterTestFramework(_ => new TestingFrameworkCapabilities(), (capabilities, serviceProvider) => new TestingFramework(capabilities, serviceProvider.GetCommandLineOptions(), diff --git a/docs/testingplatform/Source/TestingPlatformExplorer/TestingFramework/TestingFramework.cs b/docs/testingplatform/Source/TestingPlatformExplorer/TestingFramework/TestingFramework.cs index bf74a24698..0176a0c973 100644 --- a/docs/testingplatform/Source/TestingPlatformExplorer/TestingFramework/TestingFramework.cs +++ b/docs/testingplatform/Source/TestingPlatformExplorer/TestingFramework/TestingFramework.cs @@ -35,14 +35,14 @@ public TestingFramework( IConfiguration configuration, ILogger logger, IOutputDevice outputDevice, - Assembly[] assemblies) + Func assemblies) { _capabilities = (TestingFrameworkCapabilities)capabilities; _commandLineOptions = commandLineOptions; _configuration = configuration; _logger = logger; _outputDevice = outputDevice; - _assemblies = assemblies; + _assemblies = assemblies(); if (_commandLineOptions.TryGetOptionArgumentList(TestingFrameworkCommandLineOptions.DopOption, out string[]? argumentList)) { @@ -88,9 +88,9 @@ public Task CreateTestSessionAsync(CreateTestSessionCon public async Task ExecuteRequestAsync(ExecuteRequestContext context) { - if (_logger.IsEnabled(LogLevel.Information)) + if (_logger.IsEnabled(LogLevel.Debug)) { - await _logger.LogInformationAsync($"Executing request of type '{context.Request}'"); + await _logger.LogDebugAsync($"Executing request of type '{context.Request}'"); } switch (context.Request) diff --git a/docs/testingplatform/Source/TestingPlatformExplorer/TestingPlatformExplorer.csproj b/docs/testingplatform/Source/TestingPlatformExplorer/TestingPlatformExplorer.csproj index 70aefdcf34..bb091c54e7 100644 --- a/docs/testingplatform/Source/TestingPlatformExplorer/TestingPlatformExplorer.csproj +++ b/docs/testingplatform/Source/TestingPlatformExplorer/TestingPlatformExplorer.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/testingplatform/capabilities.md b/docs/testingplatform/capabilities.md index 08a8b059f1..b723631024 100644 --- a/docs/testingplatform/capabilities.md +++ b/docs/testingplatform/capabilities.md @@ -65,7 +65,7 @@ If the testing framework implements this interface and we can query it at runtim * Determine if the testing framework can supply CPU usage data `CanProvidePerTestCPUConsumption = true` * Request the testing adapter to activate this mode by invoking the `Enable()` method before the test session commences -The ipotetical code fragment inside the extension could be something like: +The hypothetical code fragment inside the extension could be something like: ```cs IServiceProvider serviceProvider = ...get service provider... diff --git a/docs/testingplatform/codesample.md b/docs/testingplatform/codesample.md index f98d0654e8..b740f1a617 100644 --- a/docs/testingplatform/codesample.md +++ b/docs/testingplatform/codesample.md @@ -1,6 +1,6 @@ # Code sample -You can find a practical example of the concepts discussed in this documentation by opening the solution located at `./Source/TestingPlatformSamples.sln`. +You can find a practical example of the concepts discussed in this documentation by opening the solution located at [./Source/TestingPlatformSamples.sln](Source). This project demonstrates a basic `TestingFramework` testing framework, showcasing how to utilize the extensibility point and various available services. diff --git a/docs/testingplatform/executionorder.md b/docs/testingplatform/executionorder.md new file mode 100644 index 0000000000..115414e530 --- /dev/null +++ b/docs/testingplatform/executionorder.md @@ -0,0 +1,21 @@ +# Testing framework & extensions execution order + +The testing platform consists of a [testing framework](itestframework.md) and any number of extensions that can operate [*in-process*](extensionintro.md) or [*out-of-process*](extensionintro.md). This document outlines the **sequence of calls** to all potential extensibility points to provide clarity on when a feature is anticipated to be invoked. + +While a *sequence* could be used to depict this, we opt for a straightforward order of invocation calls, which allows for a more comprehensive commentary on the workflow. + +1. [ITestHostEnvironmentVariableProvider.UpdateAsync](itesthostenvironmentvariableprovider.md) : Out-of-process +1. [ITestHostEnvironmentVariableProvider.ValidateTestHostEnvironmentVariablesAsync](itesthostenvironmentvariableprovider.md) : Out-of-process +1. [ITestHostProcessLifetimeHandler.BeforeTestHostProcessStartAsync](itesthostprocesslifetimehandler.md) : Out-of-process +1. Test host process start +1. [ITestHostProcessLifetimeHandler.OnTestHostProcessStartedAsync](itesthostprocesslifetimehandler.md) : Out-of-process, this event can intertwine the actions of *in-process* extensions, depending on race conditions. +1. [ITestApplicationLifecycleCallbacks.BeforeRunAsync](itestsessionlifetimehandler.md): In-process +1. [ITestSessionLifetimeHandler.OnTestSessionStartingAsync](itestsessionlifetimehandler.md): In-process +1. [ITestFramework.CreateTestSessionAsync](itestframework.md): In-process +1. [ITestFramework.ExecuteRequestAsync](itestframework.md): In-process, this method can be called one or more times. At this point, the testing framework will transmit information to the [IMessageBus](imessagebus.md) that can be utilized by the [IDataConsumer](idataconsumer.md). +1. [ITestFramework.CloseTestSessionAsync](itestframework.md): In-process +1. [ITestSessionLifetimeHandler.OnTestSessionFinishingAsync](itestsessionlifetimehandler.md): In-process +1. [ITestApplicationLifecycleCallbacks.AfterRunAsync](itestsessionlifetimehandler.md): In-process +1. In-process cleanup, involves calling dispose and [IAsyncCleanableExtension](asyncinitcleanup.md) on all extension points. +1. [ITestHostProcessLifetimeHandler.OnTestHostProcessExitedAsync](itesthostprocesslifetimehandler.md) : Out-of-process +1. Out-of-process cleanup, involves calling dispose and [IAsyncCleanableExtension](asyncinitcleanup.md) on all extension points. diff --git a/docs/testingplatform/ibannermessageownercapability.md b/docs/testingplatform/ibannermessageownercapability.md new file mode 100644 index 0000000000..a30cb68849 --- /dev/null +++ b/docs/testingplatform/ibannermessageownercapability.md @@ -0,0 +1,7 @@ +# `IBannerMessageOwnerCapability` + +An optional [test framework capability](itestframeworkcapability.md) that allows the test framework to provide the banner message to the platform. If the message is null or if the capability is not present, the platform will use its default banner message. + +This capability implementation allows to abstract away the various conditions that the test framework may need to consider to decide whether or not the banner message should be displayed. + +The platform exposes the [`IPlatformInformation` service](iplatforminformation.md) to provide some information about the platform that could be useful when building your custom banner message. diff --git a/docs/testingplatform/idataconsumer.md b/docs/testingplatform/idataconsumer.md index a6e461d197..b2926f4963 100644 --- a/docs/testingplatform/idataconsumer.md +++ b/docs/testingplatform/idataconsumer.md @@ -90,4 +90,9 @@ Finally, the api takes a `CancellationToken` which the extension is expected to > [!IMPORTANT] > It's crucial to process the payload directly within the `ConsumeAsync` method. The [IMessageBus](imessagebus.md) can manage both synchronous and asynchronous processing, coordinating the execution with the [testing framework](itestframework.md). Although the consumption process is entirely asynchronous and doesn't block the [IMessageBus.Push](imessagebus.md) at the time of writing, this is an implementation detail that may change in the future due to feature requirements. However, we aim to maintain this interface's simplicity and ensure that this method is always called once, eliminating the need for complex synchronization. Additionally, we automatically manage the scalability of the consumers. + + +> [!WARNING] +> When using `IDataConsumer` in conjunction with [ITestHostProcessLifetimeHandler](itestsessionlifetimehandler.md) within a [composite extension point](compositeextensionfactory.md), **it's crucial to disregard any data received post the execution of [ITestSessionLifetimeHandler.OnTestSessionFinishingAsync](itestsessionlifetimehandler.md)**. The `OnTestSessionFinishingAsync` is the final opportunity to process accumulated data and transmit new information to the [IMessageBus](imessagebus.md), hence, any data consumed beyond this point will not be *utilizable* by the extension. + If your extension requires intensive initialization and you need to use the async/await pattern, you can refer to the [`Async extension initialization and cleanup`](asyncinitcleanup.md). If you need to *share state* between extension points, you can refer to the [`CompositeExtensionFactory`](compositeextensionfactory.md) section. diff --git a/docs/testingplatform/imessagebus.md b/docs/testingplatform/imessagebus.md index 1ba3ddc76b..33d1428b35 100644 --- a/docs/testingplatform/imessagebus.md +++ b/docs/testingplatform/imessagebus.md @@ -37,7 +37,7 @@ Let's discuss the parameters: * `IData`: This interface serves as a placeholder where you only need to provide descriptive details such as the name and a description. The interface doesn't reveal much about the data's nature, which is intentional. It implies that the test framework and extensions can push any type of data to the bus, and this data can be consumed by any registered extension or the test framework itself. This approach facilitates the evolution of the information exchange process, preventing breaking changes when an extension is unfamiliar with new data. **It allows different versions of extensions and the test framework to operate in harmony, based on their mutual understanding**. -The opposite end of the bus is what we refer to as a [consumer](idataConsumer.md), which is subscribed to a specific type of data and can thus consume it. +The opposite end of the bus is what we refer to as a [consumer](idataconsumer.md), which is subscribed to a specific type of data and can thus consume it. > [!IMPORTANT] > Always use *await* the call to `PublishAsync`. If you don't, the `IData` might not be processed correctly by the testing platform and extensions, which could lead to subtle bugs. It's only after you've returned from the *await* that you can be assured that the `IData` has been queued for processing on the message bus. Regardless of the extension point you're working on, ensure that you've awaited all `PublishAsync` calls before exiting the extension. For example, if you're implementing the [`testing framework`](itestframework.md), you should not call `Complete` on the [requests](irequest.md) until you've awaited all `PublishAsync` calls for that specific request. diff --git a/docs/testingplatform/iplatforminformation.md b/docs/testingplatform/iplatforminformation.md new file mode 100644 index 0000000000..bb3510fad2 --- /dev/null +++ b/docs/testingplatform/iplatforminformation.md @@ -0,0 +1,3 @@ +# `IPlatformInformation` + +Provides information about the platform such as: name, version, commit hash and build date. diff --git a/docs/testingplatform/itestframework.md b/docs/testingplatform/itestframework.md index 176ea51192..ae0b1e9b2a 100644 --- a/docs/testingplatform/itestframework.md +++ b/docs/testingplatform/itestframework.md @@ -1,4 +1,4 @@ -# Implement the Microsoft.Testing.Platform.Extensions.TestFramework.ITestFramework +# The `Microsoft.Testing.Platform.Extensions.TestFramework.ITestFramework` The `Microsoft.Testing.Platform.Extensions.TestFramework.ITestFramework` is implemented by extensions that provide a test framework: @@ -78,7 +78,7 @@ public sealed class ExecuteRequestContext ```mermaid sequenceDiagram Testing platform->>ITestFramework: adapterFactory() from 'RegisterTestFramework' - ITestFramework-->>Testing platform: + ITestFramework-->>Testing platform: Testing platform->>ITestFramework: CreateTestSessionAsync(CreateTestSessionContext) ITestFramework-->>Testing platform: CreateTestSessionResult Testing platform->>ITestFramework: ExecuteRequestAsync(ExecuteRequestContext_1) @@ -113,8 +113,8 @@ For a comprehensive list of information that can be published to the testing pla `CancellationToken`: This token is utilized to interrupt the processing of a particular request. `Complete()`: As depicted in the previous sequence, the `Complete` method notifies the platform that the request has been successfully processed and all relevant information has been transmitted to the [IMessageBus](imessagebus.md). ->> [!WARNING] ->> Neglecting to invoke `Complete()` on the request will result in the test application becoming unresponsive. +> [!WARNING] +> Neglecting to invoke `Complete()` on the request will result in the test application becoming unresponsive. To customize your test framework according to your requirements or those of your users, you can use a personalized section inside the [configuration](configuration.md) file or with custom [command line options](icommandlineoptionsprovider.md). diff --git a/docs/testingplatform/itestframeworkcapability.md b/docs/testingplatform/itestframeworkcapability.md new file mode 100644 index 0000000000..1186395fe4 --- /dev/null +++ b/docs/testingplatform/itestframeworkcapability.md @@ -0,0 +1,3 @@ +# Test framework capabilities + +The platform exposes a specialized interface named `ITestFrameworkCapability` that is the base of all capabilities exposed for test frameworks. These capabilities are provided when [registering the test framework to the platform](registertestframework.md). diff --git a/docs/testingplatform/itesthostenvironmentvariableprovider.md b/docs/testingplatform/itesthostenvironmentvariableprovider.md new file mode 100644 index 0000000000..30b5b39f62 --- /dev/null +++ b/docs/testingplatform/itesthostenvironmentvariableprovider.md @@ -0,0 +1,71 @@ +# The `ITestHostEnvironmentVariableProvider` + +The `ITestHostEnvironmentVariableProvider` is an *out-of-process* extension that enables you to establish custom environment variables for the test host. Utilizing this extension point ensures that the testing platform will initiate a new host with the appropriate environment variables, as detailed in the [architecture](architecture.md) section. + +To register a custom `ITestHostEnvironmentVariableProvider`, utilize the following API: + +```cs +ITestApplicationBuilder testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); +... +testApplicationBuilder.TestHostControllers.AddEnvironmentVariableProvider(serviceProvider => + => new CustomEnvironmentVariableForTestHost()); +... +``` + +The factory utilizes the [IServiceProvider](iserviceprovider.md) to gain access to the suite of services offered by the testing platform. + +> [!IMPORTANT] +> The sequence of registration is significant, as the APIs are called in the order they were registered. + +The `ITestHostEnvironmentVariableProvider` interface includes the following methods and types: + +```cs +public interface ITestHostEnvironmentVariableProvider : ITestHostControllersExtension, IExtension +{ + Task UpdateAsync(IEnvironmentVariables environmentVariables); + Task ValidateTestHostEnvironmentVariablesAsync(IReadOnlyEnvironmentVariables environmentVariables); +} + +public interface IEnvironmentVariables : IReadOnlyEnvironmentVariables +{ + void SetVariable(EnvironmentVariable environmentVariable); + void RemoveVariable(string variable); +} + +public interface IReadOnlyEnvironmentVariables +{ + bool TryGetVariable(string variable, [NotNullWhen(true)] out OwnedEnvironmentVariable? environmentVariable); +} + +public sealed class OwnedEnvironmentVariable : EnvironmentVariable +{ + public IExtension Owner { get; } + public OwnedEnvironmentVariable(IExtension owner, string variable, string? value, bool isSecret, bool isLocked); +} + +public class EnvironmentVariable +{ + public string Variable { get; } + public string? Value { get; } + public bool IsSecret { get; } + public bool IsLocked { get; } +} +``` + +The `ITestHostEnvironmentVariableProvider` is a type of `ITestHostControllersExtension`, which serves as a base for all *test host controller* extensions. Like all other extension points, it also inherits from [IExtension](iextension.md). Therefore, like any other extension, you can choose to enable or disable it using the `IExtension.IsEnabledAsync` API. + +Let's describe the api: + +`UpdateAsync`: This update API provides an instance of the `IEnvironmentVariables` object, from which you can call the `SetVariable` or `RemoveVariable` methods. When using `SetVariable`, you must pass an object of type `EnvironmentVariable`, which requires the following specifications: + +* `Variable`: The name of the environment variable. +* `Value`: The value of the environment variable. +* `IsSecret`: This indicates whether the environment variable contains sensitive information that should not be logged or accessible via the `TryGetVariable`. +* `IsLocked`: This determines whether other `ITestHostEnvironmentVariableProvider` extensions can modify this value. + +`ValidateTestHostEnvironmentVariablesAsync`: This method is invoked after all the `UpdateAsync` methods of the registered `ITestHostEnvironmentVariableProvider` instances have been called. It allows you to *verify* the correct setup of the environment variables. It takes an object that implements `IReadOnlyEnvironmentVariables`, which provides the `TryGetVariable` method to fetch specific environment variable information with the `OwnedEnvironmentVariable` object type. After validation, you return a `ValidationResult` containing any failure reasons. + +> [!NOTE] +> The testing platform, by default, implements and registers the `SystemEnvironmentVariableProvider`. This provider loads all the *current* environment variables. As the first registered provider, it executes first, granting access to the default environment variables for all other `ITestHostEnvironmentVariableProvider` user extensions. + +If your extension requires intensive initialization and you need to use the async/await pattern, you can refer to the [`Async extension initialization and cleanup`](asyncinitcleanup.md). If you need to *share state* between extension points, you can refer to the [`CompositeExtensionFactory`](compositeextensionfactory.md) section. diff --git a/docs/testingplatform/itesthostprocesslifetimehandler.md b/docs/testingplatform/itesthostprocesslifetimehandler.md new file mode 100644 index 0000000000..565455eb3e --- /dev/null +++ b/docs/testingplatform/itesthostprocesslifetimehandler.md @@ -0,0 +1,54 @@ +# The `ITestHostProcessLifetimeHandler` + +The `ITestHostProcessLifetimeHandler` is an *out-of-process* extension that allows you to observe the test host process from an external standpoint. This ensures that your extension remains unaffected by potential crashes or hangs that could be induced by the code under test. Utilizing this extension point will prompt the testing platform to initiate a new host, as detailed in the [architecture](architecture.md) section. + +To register a custom `ITestHostProcessLifetimeHandler`, utilize the following API: + +```cs +ITestApplicationBuilder testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); +... +testApplicationBuilder.TestHostControllers.AddProcessLifetimeHandler(serviceProvider => + new CustomMonitorTestHost()); +... +``` + +The factory utilizes the [IServiceProvider](iserviceprovider.md) to gain access to the suite of services offered by the testing platform. + +> [!IMPORTANT] +> The sequence of registration is significant, as the APIs are called in the order they were registered. + +The `ITestHostProcessLifetimeHandler` interface includes the following methods: + +```cs +public interface ITestHostProcessLifetimeHandler : ITestHostControllersExtension +{ + Task BeforeTestHostProcessStartAsync(CancellationToken cancellationToken); + Task OnTestHostProcessStartedAsync(ITestHostProcessInformation testHostProcessInformation, CancellationToken cancellation); + Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testHostProcessInformation, CancellationToken cancellation); +} + +public interface ITestHostProcessInformation +{ + int PID { get; } + int ExitCode { get; } + bool HasExitedGracefully { get; } +} +``` + +The `ITestHostProcessLifetimeHandler` is a type of `ITestHostControllersExtension`, which serves as a base for all *test host controller* extensions. Like all other extension points, it also inherits from [IExtension](iextension.md). Therefore, like any other extension, you can choose to enable or disable it using the `IExtension.IsEnabledAsync` API. + +Let's describe the api: + +`BeforeTestHostProcessStartAsync`: This method is invoked prior to the testing platform initiating the test hosts. + +`OnTestHostProcessStartedAsync`: This method is invoked immediately after the test host starts. This method offers an object that implements the `ITestHostProcessInformation` interface, which provides key details about the test host process result. +> [!IMPORTANT] +> The invocation of this method does not halt the test host's execution. If you need to pause it, you should register an [*in-process*](extensionintro.md) extension such as [`ITestApplicationLifecycleCallbacks`](itestapplicationlifecyclecallbacks.md) and synchronize it with the *out-of-process* extension. + +`OnTestHostProcessExitedAsync`: This method is invoked when the test suite execution is complete. This method supplies an object that adheres to the `ITestHostProcessInformation` interface, which conveys crucial details about the outcome of the test host process. + +The `ITestHostProcessInformation` interface provides the following details: + +* `PID`: The process ID of the test host. +* `ExitCode`: The exit code of the process. This value is only available within the `OnTestHostProcessExitedAsync` method. Attempting to access it within the `OnTestHostProcessStartedAsync` method will result in an exception. +* `HasExitedGracefully`: A boolean value indicating whether the test host has crashed. If true, it signifies that the test host did not exit gracefully. diff --git a/docs/testingplatform/pillars.md b/docs/testingplatform/pillars.md index ca75f39a93..5b8463f425 100644 --- a/docs/testingplatform/pillars.md +++ b/docs/testingplatform/pillars.md @@ -13,3 +13,14 @@ The main driving factors for the evolution of the new testing platform are: * **Performant**: Finding the right balance between features and extension points to avoid bloating the runtime with non-fundamental code. The new test platform is designed to "orchestrate" a test run, rather than providing implementation details on how to do it. * **Extensible enough**: The new platform is built on extensibility points to allow for maximum customization of runtime execution. It allows you to configure the test process host, observe the test process, and consume information from the test framework within the test host process. * **Single module deploy**: The hostability feature enables a single module deploy model, where a single compilation result can be used to support all extensibility points, both out-of-process and in-process, without the need to ship different executable modules. + +The following factors should enhance the overall quality of the testing platform: + +* Shifting all to compile time aids in identifying issues prior to runtime. +* Determinism is beneficial in all aspects, as it reduces bugs and, in the event of a bug, provides a clear, straightforward stack trace that directly indicates the problem. +* Determinism facilitates straightforward reproduction of issues, independent of the execution context (such as machine setup, local environment, CI, etc.). If the issue is related to the execution context, determinism makes it evident. +* Dynamic code, often associated with "indirect execution", can result in performance degradation. By avoiding it, performance can be enhanced. +* Eliminating dynamic code simplifies the logic behind feature development. The code you see is exactly what will be executed at runtime. +* Eliminating dependencies and dynamic code guarantees compatibility with upcoming runtime features. +* Eliminating dependencies means we can run everywhere. +* Operating a self-contained testing platform without dependencies ensures that there's always a mechanism to execute user tests. This is because `Microsoft.Testing.Platform.dll` is viewed as a fundamental part of the runtime itself, ideally `System.Testing.Platform`. diff --git a/eng/Analyzers.props b/eng/Analyzers.props index 31d9a3c856..15c8273caf 100644 --- a/eng/Analyzers.props +++ b/eng/Analyzers.props @@ -12,7 +12,7 @@ - + diff --git a/eng/Build.props b/eng/Build.props index 460968206c..85cf030782 100644 --- a/eng/Build.props +++ b/eng/Build.props @@ -1,15 +1,40 @@ - + + all + + + + - + + - + - + + + + + + + + + + + + + + + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 97d3ff2079..14aa5b594d 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,33 +1,29 @@ - + https://github.com/dotnet/arcade - 67d23f4ba1813b315e7e33c71d18b63475f5c5f8 + c9efa535175049eb9cba06cae1f8c3d5dbe768a9 - + https://github.com/dotnet/arcade - 67d23f4ba1813b315e7e33c71d18b63475f5c5f8 + c9efa535175049eb9cba06cae1f8c3d5dbe768a9 https://github.com/dotnet/arcade 2fb543a45580400a559b5ae41c96a815ea14dac5 - + https://dev.azure.com/devdiv/DevDiv/_git/vs-code-coverage - eaff7ae97a30e87a8e5a64e7ff989f4a8de69439 + fbc5c8316febc4897fe37cb6a1bb20a998917a87 - + https://github.com/microsoft/testanywhere - 445bda5374781f22ebd553dbc5c52d4c9c4176eb + 4a12d8fed3ac9892d74bce1b75de8c8a5b21cbd9 - + https://github.com/microsoft/testanywhere - 0e10f1f3c9cd3f306517e037b7560c6cf2ab2e9b - - - https://github.com/microsoft/testanywhere - 0e10f1f3c9cd3f306517e037b7560c6cf2ab2e9b + 4a12d8fed3ac9892d74bce1b75de8c8a5b21cbd9 diff --git a/eng/Versions.props b/eng/Versions.props index 02eacfb3cb..7342875b96 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,14 +1,16 @@ - - 3.4.0 + + 3.5.0 + + 1.3.1 preview - 8.0.0-beta.24225.1 - 17.11.1-preview.24257.1 - 1.2.0-preview.24168.3 - 1.2.0-preview.24256.5 - 1.0.0-alpha.24256.5 + 8.0.0-beta.24360.5 + 17.11.3 + + 1.3.0 + 1.0.0-alpha.24366.3 diff --git a/eng/common/post-build/publish-using-darc.ps1 b/eng/common/post-build/publish-using-darc.ps1 index 5a3a32ea8d..238945cb5a 100644 --- a/eng/common/post-build/publish-using-darc.ps1 +++ b/eng/common/post-build/publish-using-darc.ps1 @@ -2,7 +2,6 @@ param( [Parameter(Mandatory=$true)][int] $BuildId, [Parameter(Mandatory=$true)][int] $PublishingInfraVersion, [Parameter(Mandatory=$true)][string] $AzdoToken, - [Parameter(Mandatory=$true)][string] $MaestroToken, [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro.dot.net', [Parameter(Mandatory=$true)][string] $WaitPublishingFinish, [Parameter(Mandatory=$false)][string] $ArtifactsPublishingAdditionalParameters, @@ -31,13 +30,13 @@ try { } & $darc add-build-to-channel ` - --id $buildId ` - --publishing-infra-version $PublishingInfraVersion ` - --default-channels ` - --source-branch main ` - --azdev-pat $AzdoToken ` - --bar-uri $MaestroApiEndPoint ` - --password $MaestroToken ` + --id $buildId ` + --publishing-infra-version $PublishingInfraVersion ` + --default-channels ` + --source-branch main ` + --azdev-pat "$AzdoToken" ` + --bar-uri "$MaestroApiEndPoint" ` + --ci ` @optionalParams if ($LastExitCode -ne 0) { diff --git a/eng/common/templates-official/job/publish-build-assets.yml b/eng/common/templates-official/job/publish-build-assets.yml index 589ac80a18..ba3e7df815 100644 --- a/eng/common/templates-official/job/publish-build-assets.yml +++ b/eng/common/templates-official/job/publish-build-assets.yml @@ -76,13 +76,16 @@ jobs: - task: NuGetAuthenticate@1 - - task: PowerShell@2 + - task: AzureCLI@2 displayName: Publish Build Assets inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet + azureSubscription: "Darc: Maestro Production" + scriptType: ps + scriptLocation: scriptPath + scriptPath: $(Build.SourcesDirectory)/eng/common/sdk-task.ps1 + arguments: > + -task PublishBuildAssets -restore -msbuildEngine dotnet /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests' - /p:BuildAssetRegistryToken=$(MaestroAccessToken) /p:MaestroApiEndpoint=https://maestro-prod.westus2.cloudapp.azure.com /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }} /p:OfficialBuildId=$(Build.BuildNumber) @@ -137,14 +140,16 @@ jobs: BARBuildId: ${{ parameters.BARBuildId }} PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - task: PowerShell@2 + - task: AzureCLI@2 displayName: Publish Using Darc inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: -BuildId $(BARBuildId) + azureSubscription: "Darc: Maestro Production" + scriptType: ps + scriptLocation: scriptPath + scriptPath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + arguments: -BuildId $(BARBuildId) -PublishingInfraVersion 3 -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' - -MaestroToken '$(MaestroApiAccessToken)' -WaitPublishingFinish true -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' diff --git a/eng/common/templates-official/job/source-build.yml b/eng/common/templates-official/job/source-build.yml index f193dfbe23..f983033bb0 100644 --- a/eng/common/templates-official/job/source-build.yml +++ b/eng/common/templates-official/job/source-build.yml @@ -31,6 +31,12 @@ parameters: # container and pool. platform: {} + # If set to true and running on a non-public project, + # Internal blob storage locations will be enabled. + # This is not enabled by default because many repositories do not need internal sources + # and do not need to have the required service connections approved in the pipeline. + enableInternalSources: false + jobs: - job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }} displayName: Source-Build (${{ parameters.platform.name }}) @@ -62,6 +68,8 @@ jobs: clean: all steps: + - ${{ if eq(parameters.enableInternalSources, true) }}: + - template: /eng/common/templates-official/steps/enable-internal-runtimes.yml - template: /eng/common/templates-official/steps/source-build.yml parameters: platform: ${{ parameters.platform }} diff --git a/eng/common/templates-official/job/source-index-stage1.yml b/eng/common/templates-official/job/source-index-stage1.yml index f0513aee5b..60dfb6b2d1 100644 --- a/eng/common/templates-official/job/source-index-stage1.yml +++ b/eng/common/templates-official/job/source-index-stage1.yml @@ -1,6 +1,7 @@ parameters: runAsPublic: false - sourceIndexPackageVersion: 1.0.1-20230228.2 + sourceIndexUploadPackageVersion: 2.0.0-20240502.12 + sourceIndexProcessBinlogPackageVersion: 1.0.1-20240129.2 sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" preSteps: [] @@ -14,14 +15,14 @@ jobs: dependsOn: ${{ parameters.dependsOn }} condition: ${{ parameters.condition }} variables: - - name: SourceIndexPackageVersion - value: ${{ parameters.sourceIndexPackageVersion }} + - name: SourceIndexUploadPackageVersion + value: ${{ parameters.sourceIndexUploadPackageVersion }} + - name: SourceIndexProcessBinlogPackageVersion + value: ${{ parameters.sourceIndexProcessBinlogPackageVersion }} - name: SourceIndexPackageSource value: ${{ parameters.sourceIndexPackageSource }} - name: BinlogPath value: ${{ parameters.binlogPath }} - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: source-dot-net stage1 variables - template: /eng/common/templates-official/variables/pool-providers.yml ${{ if ne(parameters.pool, '') }}: @@ -41,16 +42,16 @@ jobs: - ${{ preStep }} - task: UseDotNet@2 - displayName: Use .NET Core SDK 6 + displayName: Use .NET 8 SDK inputs: packageType: sdk - version: 6.0.x + version: 8.0.x installationPath: $(Agent.TempDirectory)/dotnet workingDirectory: $(Agent.TempDirectory) - script: | - $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools - $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(sourceIndexProcessBinlogPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(sourceIndexUploadPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools displayName: Download Tools # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. workingDirectory: $(Agent.TempDirectory) @@ -62,7 +63,21 @@ jobs: displayName: Process Binlog into indexable sln - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) + - task: AzureCLI@2 + displayName: Get stage 1 auth token + inputs: + azureSubscription: 'SourceDotNet Stage1 Publish' + addSpnToEnvironment: true + scriptType: 'ps' + scriptLocation: 'inlineScript' + inlineScript: | + echo "##vso[task.setvariable variable=ARM_CLIENT_ID;issecret=true]$env:servicePrincipalId" + echo "##vso[task.setvariable variable=ARM_ID_TOKEN;issecret=true]$env:idToken" + echo "##vso[task.setvariable variable=ARM_TENANT_ID;issecret=true]$env:tenantId" + + - script: | + az login --service-principal -u $(ARM_CLIENT_ID) --tenant $(ARM_TENANT_ID) --allow-no-subscriptions --federated-token $(ARM_ID_TOKEN) + displayName: "Login to Azure" + + - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) -s netsourceindexstage1 -b stage1 displayName: Upload stage1 artifacts to source index - env: - BLOB_CONTAINER_URL: $(source-dot-net-stage1-blob-container-url) diff --git a/eng/common/templates-official/jobs/source-build.yml b/eng/common/templates-official/jobs/source-build.yml index 08e5db9bb1..5cf6a269c0 100644 --- a/eng/common/templates-official/jobs/source-build.yml +++ b/eng/common/templates-official/jobs/source-build.yml @@ -21,6 +21,12 @@ parameters: # one job runs on 'defaultManagedPlatform'. platforms: [] + # If set to true and running on a non-public project, + # Internal nuget and blob storage locations will be enabled. + # This is not enabled by default because many repositories do not need internal sources + # and do not need to have the required service connections approved in the pipeline. + enableInternalSources: false + jobs: - ${{ if ne(parameters.allCompletedJobId, '') }}: @@ -38,9 +44,11 @@ jobs: parameters: jobNamePrefix: ${{ parameters.jobNamePrefix }} platform: ${{ platform }} + enableInternalSources: ${{ parameters.enableInternalSources }} - ${{ if eq(length(parameters.platforms), 0) }}: - template: /eng/common/templates-official/job/source-build.yml parameters: jobNamePrefix: ${{ parameters.jobNamePrefix }} platform: ${{ parameters.defaultManagedPlatform }} + enableInternalSources: ${{ parameters.enableInternalSources }} diff --git a/eng/common/templates-official/post-build/post-build.yml b/eng/common/templates-official/post-build/post-build.yml index da1f40958b..0dfa387e7b 100644 --- a/eng/common/templates-official/post-build/post-build.yml +++ b/eng/common/templates-official/post-build/post-build.yml @@ -272,14 +272,16 @@ stages: - task: NuGetAuthenticate@1 - - task: PowerShell@2 + - task: AzureCLI@2 displayName: Publish Using Darc inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + azureSubscription: "Darc: Maestro Production" + scriptType: ps + scriptLocation: scriptPath + scriptPath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 arguments: -BuildId $(BARBuildId) -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' - -MaestroToken '$(MaestroApiAccessToken)' -WaitPublishingFinish true -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' diff --git a/eng/common/templates-official/steps/enable-internal-runtimes.yml b/eng/common/templates-official/steps/enable-internal-runtimes.yml new file mode 100644 index 0000000000..93a8394a66 --- /dev/null +++ b/eng/common/templates-official/steps/enable-internal-runtimes.yml @@ -0,0 +1,28 @@ +# Obtains internal runtime download credentials and populates the 'dotnetbuilds-internal-container-read-token-base64' +# variable with the base64-encoded SAS token, by default + +parameters: +- name: federatedServiceConnection + type: string + default: 'dotnetbuilds-internal-read' +- name: outputVariableName + type: string + default: 'dotnetbuilds-internal-container-read-token-base64' +- name: expiryInHours + type: number + default: 1 +- name: base64Encode + type: boolean + default: true + +steps: +- ${{ if ne(variables['System.TeamProject'], 'public') }}: + - template: /eng/common/templates-official/steps/get-delegation-sas.yml + parameters: + federatedServiceConnection: ${{ parameters.federatedServiceConnection }} + outputVariableName: ${{ parameters.outputVariableName }} + expiryInHours: ${{ parameters.expiryInHours }} + base64Encode: ${{ parameters.base64Encode }} + storageAccount: dotnetbuilds + container: internal + permissions: rl diff --git a/eng/common/templates-official/steps/get-delegation-sas.yml b/eng/common/templates-official/steps/get-delegation-sas.yml new file mode 100644 index 0000000000..c0e8f91317 --- /dev/null +++ b/eng/common/templates-official/steps/get-delegation-sas.yml @@ -0,0 +1,43 @@ +parameters: +- name: federatedServiceConnection + type: string +- name: outputVariableName + type: string +- name: expiryInHours + type: number + default: 1 +- name: base64Encode + type: boolean + default: false +- name: storageAccount + type: string +- name: container + type: string +- name: permissions + type: string + default: 'rl' + +steps: +- task: AzureCLI@2 + displayName: 'Generate delegation SAS Token for ${{ parameters.storageAccount }}/${{ parameters.container }}' + inputs: + azureSubscription: ${{ parameters.federatedServiceConnection }} + scriptType: 'pscore' + scriptLocation: 'inlineScript' + inlineScript: | + # Calculate the expiration of the SAS token and convert to UTC + $expiry = (Get-Date).AddHours(${{ parameters.expiryInHours }}).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") + + $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv + + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to generate SAS token." + exit 1 + } + + if ('${{ parameters.base64Encode }}' -eq 'true') { + $sas = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($sas)) + } + + Write-Host "Setting '${{ parameters.outputVariableName }}' with the access token value" + Write-Host "##vso[task.setvariable variable=${{ parameters.outputVariableName }};issecret=true]$sas" diff --git a/eng/common/templates-official/steps/get-federated-access-token.yml b/eng/common/templates-official/steps/get-federated-access-token.yml new file mode 100644 index 0000000000..e3786cef6d --- /dev/null +++ b/eng/common/templates-official/steps/get-federated-access-token.yml @@ -0,0 +1,28 @@ +parameters: +- name: federatedServiceConnection + type: string +- name: outputVariableName + type: string +# Resource to get a token for. Common values include: +# - '499b84ac-1321-427f-aa17-267ca6975798' for Azure DevOps +# - 'https://storage.azure.com/' for storage +# Defaults to Azure DevOps +- name: resource + type: string + default: '499b84ac-1321-427f-aa17-267ca6975798' + +steps: +- task: AzureCLI@2 + displayName: 'Getting federated access token for feeds' + inputs: + azureSubscription: ${{ parameters.federatedServiceConnection }} + scriptType: 'pscore' + scriptLocation: 'inlineScript' + inlineScript: | + $accessToken = az account get-access-token --query accessToken --resource ${{ parameters.resource }} --output tsv + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to get access token for resource '${{ parameters.resource }}'" + exit 1 + } + Write-Host "Setting '${{ parameters.outputVariableName }}' with the access token value" + Write-Host "##vso[task.setvariable variable=${{ parameters.outputVariableName }};issecret=true]$accessToken" diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml index 8ec0151def..57a41f0a3e 100644 --- a/eng/common/templates/job/publish-build-assets.yml +++ b/eng/common/templates/job/publish-build-assets.yml @@ -74,13 +74,16 @@ jobs: - task: NuGetAuthenticate@1 - - task: PowerShell@2 + - task: AzureCLI@2 displayName: Publish Build Assets inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet + azureSubscription: "Darc: Maestro Production" + scriptType: ps + scriptLocation: scriptPath + scriptPath: $(Build.SourcesDirectory)/eng/common/sdk-task.ps1 + arguments: > + -task PublishBuildAssets -restore -msbuildEngine dotnet /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests' - /p:BuildAssetRegistryToken=$(MaestroAccessToken) /p:MaestroApiEndpoint=https://maestro.dot.net /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }} /p:OfficialBuildId=$(Build.BuildNumber) @@ -133,14 +136,16 @@ jobs: BARBuildId: ${{ parameters.BARBuildId }} PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - task: PowerShell@2 + - task: AzureCLI@2 displayName: Publish Using Darc inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: -BuildId $(BARBuildId) + azureSubscription: "Darc: Maestro Production" + scriptType: ps + scriptLocation: scriptPath + scriptPath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + arguments: -BuildId $(BARBuildId) -PublishingInfraVersion 3 -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' - -MaestroToken '$(MaestroApiAccessToken)' -WaitPublishingFinish true -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' diff --git a/eng/common/templates/job/source-build.yml b/eng/common/templates/job/source-build.yml index 8a3deef2b7..c0ff472b69 100644 --- a/eng/common/templates/job/source-build.yml +++ b/eng/common/templates/job/source-build.yml @@ -31,6 +31,12 @@ parameters: # container and pool. platform: {} + # If set to true and running on a non-public project, + # Internal blob storage locations will be enabled. + # This is not enabled by default because many repositories do not need internal sources + # and do not need to have the required service connections approved in the pipeline. + enableInternalSources: false + jobs: - job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }} displayName: Source-Build (${{ parameters.platform.name }}) @@ -61,6 +67,8 @@ jobs: clean: all steps: + - ${{ if eq(parameters.enableInternalSources, true) }}: + - template: /eng/common/templates/steps/enable-internal-runtimes.yml - template: /eng/common/templates/steps/source-build.yml parameters: platform: ${{ parameters.platform }} diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml index b98202aa02..0b6bb89dc7 100644 --- a/eng/common/templates/job/source-index-stage1.yml +++ b/eng/common/templates/job/source-index-stage1.yml @@ -1,6 +1,7 @@ parameters: runAsPublic: false - sourceIndexPackageVersion: 1.0.1-20230228.2 + sourceIndexUploadPackageVersion: 2.0.0-20240502.12 + sourceIndexProcessBinlogPackageVersion: 1.0.1-20240129.2 sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" preSteps: [] @@ -14,14 +15,14 @@ jobs: dependsOn: ${{ parameters.dependsOn }} condition: ${{ parameters.condition }} variables: - - name: SourceIndexPackageVersion - value: ${{ parameters.sourceIndexPackageVersion }} + - name: SourceIndexUploadPackageVersion + value: ${{ parameters.sourceIndexUploadPackageVersion }} + - name: SourceIndexProcessBinlogPackageVersion + value: ${{ parameters.sourceIndexProcessBinlogPackageVersion }} - name: SourceIndexPackageSource value: ${{ parameters.sourceIndexPackageSource }} - name: BinlogPath value: ${{ parameters.binlogPath }} - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: source-dot-net stage1 variables - template: /eng/common/templates/variables/pool-providers.yml ${{ if ne(parameters.pool, '') }}: @@ -40,16 +41,16 @@ jobs: - ${{ preStep }} - task: UseDotNet@2 - displayName: Use .NET Core SDK 6 + displayName: Use .NET 8 SDK inputs: packageType: sdk - version: 6.0.x + version: 8.0.x installationPath: $(Agent.TempDirectory)/dotnet workingDirectory: $(Agent.TempDirectory) - script: | - $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools - $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(sourceIndexProcessBinlogPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(sourceIndexUploadPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools displayName: Download Tools # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. workingDirectory: $(Agent.TempDirectory) @@ -61,7 +62,21 @@ jobs: displayName: Process Binlog into indexable sln - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) + - task: AzureCLI@2 + displayName: Get stage 1 auth token + inputs: + azureSubscription: 'SourceDotNet Stage1 Publish' + addSpnToEnvironment: true + scriptType: 'ps' + scriptLocation: 'inlineScript' + inlineScript: | + echo "##vso[task.setvariable variable=ARM_CLIENT_ID;issecret=true]$env:servicePrincipalId" + echo "##vso[task.setvariable variable=ARM_ID_TOKEN;issecret=true]$env:idToken" + echo "##vso[task.setvariable variable=ARM_TENANT_ID;issecret=true]$env:tenantId" + + - script: | + az login --service-principal -u $(ARM_CLIENT_ID) --tenant $(ARM_TENANT_ID) --allow-no-subscriptions --federated-token $(ARM_ID_TOKEN) + displayName: "Login to Azure" + + - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) -s netsourceindexstage1 -b stage1 displayName: Upload stage1 artifacts to source index - env: - BLOB_CONTAINER_URL: $(source-dot-net-stage1-blob-container-url) diff --git a/eng/common/templates/jobs/source-build.yml b/eng/common/templates/jobs/source-build.yml index a15b07eb51..5f46bfa895 100644 --- a/eng/common/templates/jobs/source-build.yml +++ b/eng/common/templates/jobs/source-build.yml @@ -21,6 +21,12 @@ parameters: # one job runs on 'defaultManagedPlatform'. platforms: [] + # If set to true and running on a non-public project, + # Internal nuget and blob storage locations will be enabled. + # This is not enabled by default because many repositories do not need internal sources + # and do not need to have the required service connections approved in the pipeline. + enableInternalSources: false + jobs: - ${{ if ne(parameters.allCompletedJobId, '') }}: @@ -38,9 +44,11 @@ jobs: parameters: jobNamePrefix: ${{ parameters.jobNamePrefix }} platform: ${{ platform }} + enableInternalSources: ${{ parameters.enableInternalSources }} - ${{ if eq(length(parameters.platforms), 0) }}: - template: /eng/common/templates/job/source-build.yml parameters: jobNamePrefix: ${{ parameters.jobNamePrefix }} platform: ${{ parameters.defaultManagedPlatform }} + enableInternalSources: ${{ parameters.enableInternalSources }} diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml index aba44a25a3..2db4933468 100644 --- a/eng/common/templates/post-build/post-build.yml +++ b/eng/common/templates/post-build/post-build.yml @@ -268,14 +268,16 @@ stages: - task: NuGetAuthenticate@1 - - task: PowerShell@2 + - task: AzureCLI@2 displayName: Publish Using Darc inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + azureSubscription: "Darc: Maestro Production" + scriptType: ps + scriptLocation: scriptPath + scriptPath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 arguments: -BuildId $(BARBuildId) -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' - -MaestroToken '$(MaestroApiAccessToken)' -WaitPublishingFinish true -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' diff --git a/eng/common/templates/post-build/setup-maestro-vars.yml b/eng/common/templates/post-build/setup-maestro-vars.yml index 0c87f149a4..64b9abc685 100644 --- a/eng/common/templates/post-build/setup-maestro-vars.yml +++ b/eng/common/templates/post-build/setup-maestro-vars.yml @@ -11,13 +11,14 @@ steps: artifactName: ReleaseConfigs checkDownloadedFiles: true - - task: PowerShell@2 + - task: AzureCLI@2 name: setReleaseVars displayName: Set Release Configs Vars inputs: - targetType: inline - pwsh: true - script: | + azureSubscription: "Darc: Maestro Production" + scriptType: pscore + scriptLocation: inlineScript + inlineScript: | try { if (!$Env:PromoteToMaestroChannels -or $Env:PromoteToMaestroChannels.Trim() -eq '') { $Content = Get-Content $(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt @@ -31,15 +32,16 @@ steps: $AzureDevOpsBuildId = $Env:Build_BuildId } else { - $buildApiEndpoint = "${Env:MaestroApiEndPoint}/api/builds/${Env:BARBuildId}?api-version=${Env:MaestroApiVersion}" + . $(Build.SourcesDirectory)\eng\common\tools.ps1 + $darc = Get-Darc + $buildInfo = & $darc get-build ` + --id ${{ parameters.BARBuildId }} ` + --extended ` + --output-format json ` + --ci ` + | convertFrom-Json - $apiHeaders = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]' - $apiHeaders.Add('Accept', 'application/json') - $apiHeaders.Add('Authorization',"Bearer ${Env:MAESTRO_API_TOKEN}") - - $buildInfo = try { Invoke-WebRequest -Method Get -Uri $buildApiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" } - - $BarId = $Env:BARBuildId + $BarId = ${{ parameters.BARBuildId }} $Channels = $Env:PromoteToMaestroChannels -split "," $Channels = $Channels -join "][" $Channels = "[$Channels]" @@ -65,6 +67,4 @@ steps: exit 1 } env: - MAESTRO_API_TOKEN: $(MaestroApiAccessToken) - BARBuildId: ${{ parameters.BARBuildId }} PromoteToMaestroChannels: ${{ parameters.PromoteToChannelIds }} diff --git a/eng/common/templates/steps/enable-internal-runtimes.yml b/eng/common/templates/steps/enable-internal-runtimes.yml new file mode 100644 index 0000000000..54dc9416c5 --- /dev/null +++ b/eng/common/templates/steps/enable-internal-runtimes.yml @@ -0,0 +1,28 @@ +# Obtains internal runtime download credentials and populates the 'dotnetbuilds-internal-container-read-token-base64' +# variable with the base64-encoded SAS token, by default + +parameters: +- name: federatedServiceConnection + type: string + default: 'dotnetbuilds-internal-read' +- name: outputVariableName + type: string + default: 'dotnetbuilds-internal-container-read-token-base64' +- name: expiryInHours + type: number + default: 1 +- name: base64Encode + type: boolean + default: true + +steps: +- ${{ if ne(variables['System.TeamProject'], 'public') }}: + - template: /eng/common/templates/steps/get-delegation-sas.yml + parameters: + federatedServiceConnection: ${{ parameters.federatedServiceConnection }} + outputVariableName: ${{ parameters.outputVariableName }} + expiryInHours: ${{ parameters.expiryInHours }} + base64Encode: ${{ parameters.base64Encode }} + storageAccount: dotnetbuilds + container: internal + permissions: rl diff --git a/eng/common/templates/steps/get-delegation-sas.yml b/eng/common/templates/steps/get-delegation-sas.yml new file mode 100644 index 0000000000..c0e8f91317 --- /dev/null +++ b/eng/common/templates/steps/get-delegation-sas.yml @@ -0,0 +1,43 @@ +parameters: +- name: federatedServiceConnection + type: string +- name: outputVariableName + type: string +- name: expiryInHours + type: number + default: 1 +- name: base64Encode + type: boolean + default: false +- name: storageAccount + type: string +- name: container + type: string +- name: permissions + type: string + default: 'rl' + +steps: +- task: AzureCLI@2 + displayName: 'Generate delegation SAS Token for ${{ parameters.storageAccount }}/${{ parameters.container }}' + inputs: + azureSubscription: ${{ parameters.federatedServiceConnection }} + scriptType: 'pscore' + scriptLocation: 'inlineScript' + inlineScript: | + # Calculate the expiration of the SAS token and convert to UTC + $expiry = (Get-Date).AddHours(${{ parameters.expiryInHours }}).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") + + $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv + + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to generate SAS token." + exit 1 + } + + if ('${{ parameters.base64Encode }}' -eq 'true') { + $sas = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($sas)) + } + + Write-Host "Setting '${{ parameters.outputVariableName }}' with the access token value" + Write-Host "##vso[task.setvariable variable=${{ parameters.outputVariableName }};issecret=true]$sas" diff --git a/eng/common/templates/steps/get-federated-access-token.yml b/eng/common/templates/steps/get-federated-access-token.yml new file mode 100644 index 0000000000..c8c49cc0e8 --- /dev/null +++ b/eng/common/templates/steps/get-federated-access-token.yml @@ -0,0 +1,28 @@ +parameters: +- name: federatedServiceConnection + type: string +- name: outputVariableName + type: string +# Resource to get a token for. Common values include: +# - '499b84ac-1321-427f-aa17-267ca6975798' for Azure DevOps +# - 'https://storage.azure.com/' for storage +# Defaults to Azure DevOps +- name: resource + type: string + default: '499b84ac-1321-427f-aa17-267ca6975798' + +steps: +- task: AzureCLI@2 + displayName: 'Getting federated access token for feeds' + inputs: + azureSubscription: ${{ parameters.federatedServiceConnection }} + scriptType: 'pscore' + scriptLocation: 'inlineScript' + inlineScript: | + $accessToken = az account get-access-token --query accessToken --resource ${{ parameters.resource }} --output tsv + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to get access token for resource '${{ parameters.resource }}'" + exit 1 + } + Write-Host "Setting '${{ parameters.outputVariableName }}' with the access token value" + Write-Host "##vso[task.setvariable variable=${{ parameters.outputVariableName }};issecret=true]$accessToken" \ No newline at end of file diff --git a/eng/generate-changelog-release.ps1 b/eng/generate-changelog-release.ps1 new file mode 100644 index 0000000000..4c4ab0a3eb --- /dev/null +++ b/eng/generate-changelog-release.ps1 @@ -0,0 +1,183 @@ +<# +.SYNOPSIS + Create release notes link and changelog entries for MSTest and Testing Platform. + +.EXAMPLE + Assuming you are on branch rel/3.4 and you want to create the release notes, run + .\write-release-notes -MSTestVersion 3.4.0 -PlatformVersion 1.2.0 + it will create release notes between last commit of current branch and last release +#> + +[CmdletBinding()] +param +( + [Parameter(Mandatory=$true)] + [ValidatePattern("^\d+\.\d+\.\d+(-preview-\d{8}-\d{2})?$")][string] $MSTestVersion, + [Parameter(Mandatory=$true)] + [ValidatePattern("^\d+\.\d+\.\d+(-preview-\d{8}-\d{2})?$")][string] $PlatformVersion +) + +$Path = "." +$repoUrl = $(if ((git -C $Path remote -v) -match "upstream") { + git -C $Path remote get-url --push upstream + } + else { + git -C $Path remote get-url --push origin + }) -replace "\.git$" + +# list all tags on this branch ordered by creator date to get the latest, stable or pre-release tag. +# For stable release we choose only tags without any dash, for pre-release we choose all tags. +$tags = git -C $Path tag -l --sort=refname | Where-Object { $_ -match "v\d+\.\d+\.\d+.*" -and (-not $Stable -or $_ -notlike '*-*') } + +if ([string]::IsNullOrWhiteSpace($MSTestVersion)) { + # normally we show changes between the latest two tags + $start, $end = $tags | Select-Object -Last 2 + Write-Host "$start -- $end" + $tag = $end +} +else { + # in CI we don't have the tag yet, so we show changes between the most recent tag, and this commit + # we figure out the tag from the package version that is set by vsts-prebuild + $start = $tags | Select-Object -Last 1 + $end = git -C $Path rev-parse HEAD + $tag = "v$MSTestVersion" +} + +# # override the tags to use if you need +# $start = "v16.8.0-preview-20200812-03" +# $end = $tag = "v16.8.0-preview-20200921-01" + +Write-Host "Generating release notes for $start..$end$(if ($HasPackageVersion) { " (expected tag: $tag)" })" + +$sourceBranch = $branch = git -C $Path rev-parse --abbrev-ref HEAD +if ($sourceBranch -eq "HEAD") { + # when CI checks out just the single commit, https://docs.microsoft.com/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml + $sourceBranch = $env:BUILD_SOURCEBRANCH -replace "^refs/heads/" +} + +if ([string]::IsNullOrWhiteSpace($branch)) { + throw "Branch is null or empty!" +} + +if ([string]::IsNullOrWhiteSpace($sourceBranch)) { + throw "SourceBranch is null or empty!" +} + +Write-Host "Branch is $branch" +Write-Host "SourceBranch is $sourceBranch" + +$discard = @( + "^Update dependencies from https:\/\/", + "^\[.+\] Update dependencies from", + "^LEGO: Pull request from lego", + "^Localized file check-in by OneLocBuild Task:", + "^Juno: check in to lego" +) -join "|" + +$prUrl = "$repoUrl/pull/" +# $tagVersionNumber = $tag -replace '^v' +# using .. because I want to know the changes that are on this branch, but don't care about the changes that I don't have https://stackoverflow.com/a/24186641/3065397 +$log = (git -C $Path log "$start..$end" --oneline --pretty="format:%s" --first-parent) +$issues = $log | ForEach-Object { + if ($_ -notmatch $discard) { + if ($_ -match '^(?.+)\s\(#(?\d+)\)?$') { + $message = "* $($matches.message)" + if ($matches.pr) { + $pr = $matches.pr + $message += " [#$pr]($prUrl$pr)" + } + + if ($_ -like 'fix *') { + [pscustomobject]@{ category = "fix"; text = $message } + } elseif ($_ -like 'add *') { + [pscustomobject]@{ category = "add"; text = $message } + } else { + [pscustomobject]@{ category = "unknown"; text = "* $_" } + } + } + else { + [pscustomobject]@{ category = "unknown"; text = "* $_" } + } + } +} | Group-Object -Property category -AsHashTable +$date = Get-Date -Format "yyyy-MM-dd" +$output = @" +------------------------------- +MSTest Version: $MSTestVersion +------------------------------- + +See the release notes [here](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md#$MSTestVersion). + +------------------------------- + +## [$MSTestVersion] - $date + +See full log [here]($repoUrl/compare/$start...$tag) + +### Added + +$($issues.add.text -join "`n") + +### Fixed + +$($issues.fix.text -join "`n") + +### TO CLASSIFY + +$($issues.unknown.text -join "`n") + +### Artifacts + +* MSTest: [$MSTestVersion](https://www.nuget.org/packages/MSTest/$MSTestVersion) +* MSTest.TestFramework: [$MSTestVersion](https://www.nuget.org/packages/MSTest.TestFramework/$MSTestVersion) +* MSTest.TestAdapter: [$MSTestVersion](https://www.nuget.org/packages/MSTest.TestAdapter/$MSTestVersion) +* MSTest.Analyzers: [$MSTestVersion](https://www.nuget.org/packages/MSTest.Analyzers/$MSTestVersion) +* MSTest.Sdk: [$MSTestVersion](https://www.nuget.org/packages/MSTest.Sdk/$MSTestVersion) +* Microsoft.Testing.Extensions.CrashDump: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/$PlatformVersion) +* Microsoft.Testing.Extensions.HangDump: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/$PlatformVersion) +* Microsoft.Testing.Extensions.HotReload: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/$PlatformVersion) +* Microsoft.Testing.Extensions.Retry: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/$PlatformVersion) +* Microsoft.Testing.Extensions.TrxReport: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/$PlatformVersion) + +------------------------------- +Testing Platform Version: $PlatformVersion +------------------------------- + +See the release notes [here](https://github.com/microsoft/testfx/blob/main/docs/Changelog-TestingPlatform.md#$PlatformVersion). + +------------------------------- + +## [$PlatformVersion] - $date + +See full log [here]($repoUrl/compare/$start...$tag) + +### Added + +$($issues.add.text -join "`n") + +### Fixed + +$($issues.fix.text -join "`n") + +### TO CLASSIFY + +$($issues.unknown.text -join "`n") + +### Artifacts + + +* Microsoft.Testing.Extensions.CrashDump: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.CrashDump/$PlatformVersion) +* Microsoft.Testing.Extensions.HangDump: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HangDump/$PlatformVersion) +* Microsoft.Testing.Extensions.HotReload: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.HotReload/$PlatformVersion) +* Microsoft.Testing.Extensions.Retry: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Retry/$PlatformVersion) +* Microsoft.Testing.Extensions.Telemetry: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.Telemetry/$PlatformVersion) +* Microsoft.Testing.Extensions.TrxReport: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport/$PlatformVersion) +* Microsoft.Testing.Extensions.TrxReport.Abstractions: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport.Abstractions/$PlatformVersion) +* Microsoft.Testing.Extensions.VSTestBridge: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Extensions.VSTestBridge/$PlatformVersion) +* Microsoft.Testing.Platform: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Platform/$PlatformVersion) +* Microsoft.Testing.Platform.MSBuild: [$PlatformVersion](https://www.nuget.org/packages/Microsoft.Testing.Platform.MSBuild/$PlatformVersion) + +"@ + +$output +$output | clip diff --git a/eng/install-procdump.ps1 b/eng/install-procdump.ps1 new file mode 100644 index 0000000000..f3d5164022 --- /dev/null +++ b/eng/install-procdump.ps1 @@ -0,0 +1,99 @@ +param( + [switch]$Force +) + +function Download { + <# + .SYNOPSIS + Downloads a given uri and saves it to outputFile + .DESCRIPTION + Downloads a given uri and saves it to outputFile + PARAMETER uri + The uri to fetch +.PARAMETER outputFile + The outputh file path to save the uri +#> + param( + [Parameter(Mandatory = $true)] + $uri, + + [Parameter(Mandatory = $true)] + $outputFile + ) + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit + + $maxRetries = 5 + $retries = 1 + + while ($true) { + try { + Write-Host "GET $uri" + Invoke-WebRequest $uri -OutFile $outputFile + break + } + catch { + Write-Host "Failed to download '$uri'" + $error = $_.Exception.Message + } + + if (++$retries -le $maxRetries) { + Write-Warning $error -ErrorAction Continue + $delayInSeconds = [math]::Pow(2, $retries) - 1 # Exponential backoff + Write-Host "Retrying. Waiting for $delayInSeconds seconds before next attempt ($retries of $maxRetries)." + Start-Sleep -Seconds $delayInSeconds + } + else { + Write-Error $error -ErrorAction Continue + throw "Unable to download file in $maxRetries attempts." + } + } + + Write-Host "Download of '$uri' complete, saved to $outputFile..." + +} + +function Install-Procdump { + <# +.SYNOPSIS + Installs ProcDump into a folder in this repo. +.DESCRIPTION + This script downloads and extracts the ProcDump. +.PARAMETER Force + Overwrite the existing installation +#> + param( + [switch]$Force + ) + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' # Workaround PowerShell/PowerShell#2138 + + Set-StrictMode -Version 1 + + $repoRoot = Resolve-Path "$PSScriptRoot\..\.." + $installDir = "$repoRoot\.tools\ProcDump\" + + if (Test-Path "$installDir\procdump.exe") { + if ($Force) { + Remove-Item -Force -Recurse $installDir + } + else { + Write-Host "ProcDump already installed to $installDir. Exiting without action. Call this script again with -Force to overwrite." + exit 0 + } + } + + mkdir $installDir -ea Ignore | out-null + Write-Host "Starting ProcDump download" + Download "https://download.sysinternals.com/files/Procdump.zip" "$installDir/ProcDump.zip" + Write-Host "Done downloading ProcDump" + Expand-Archive "$installDir/ProcDump.zip" -d "$installDir" + Write-Host "Expanded ProcDump to $installDir" + + if ($env:TF_BUILD) { + Write-Host "##vso[task.setvariable variable=PROCDUMP_PATH]$installDir" + Write-Host "##vso[task.prependpath]$installDir" + } +} + +Install-Procdump -Force:$Force \ No newline at end of file diff --git a/eng/write-release-notes.ps1 b/eng/write-release-notes.ps1 deleted file mode 100644 index 9486f7723c..0000000000 --- a/eng/write-release-notes.ps1 +++ /dev/null @@ -1,120 +0,0 @@ -<# -.SYNOPSIS - Create release notes for current project using either last commit to last tag or between last 2 tags. - -.EXAMPLE - Assuming you are on branch rel/17.4 and you want to create the release notes, run - .\write-release-notes -PackageVersion 17.4.0 - it will create release notes between last commit of current branch and last release - - If you want to generate the release notes between 2 tags, use simply - .\write-release-notes -#> - -[CmdletBinding()] -param -( - [string] $Path = ".", - [ValidatePattern("^\d+\.\d+\.\d+(-preview-\d{8}-\d{2})?$")][string] $PackageVersion -) - -$repoUrl = $(if ((git -C $Path remote -v) -match "upstream") { - git -C $Path remote get-url --push upstream - } - else { - git -C $Path remote get-url --push origin - }) -replace "\.git$" - -# list all tags on this branch ordered by creator date to get the latest, stable or pre-release tag. -# For stable release we choose only tags without any dash, for pre-release we choose all tags. -$tags = git -C $Path tag -l --sort=refname | Where-Object { $_ -match "v\d+\.\d+\.\d+.*" -and (-not $Stable -or $_ -notlike '*-*') } - -if ([string]::IsNullOrWhiteSpace($PackageVersion)) { - # normally we show changes between the latest two tags - $start, $end = $tags | Select-Object -Last 2 - Write-Host "$start -- $end" - $tag = $end -} -else { - # in CI we don't have the tag yet, so we show changes between the most recent tag, and this commit - # we figure out the tag from the package version that is set by vsts-prebuild - $start = $tags | Select-Object -Last 1 - $end = git -C $Path rev-parse HEAD - $tag = "v$PackageVersion" -} - -# # override the tags to use if you need -# $start = "v16.8.0-preview-20200812-03" -# $end = $tag = "v16.8.0-preview-20200921-01" - -Write-Host "Generating release notes for $start..$end$(if ($HasPackageVersion) { " (expected tag: $tag)" })" - -$sourceBranch = $branch = git -C $Path rev-parse --abbrev-ref HEAD -if ($sourceBranch -eq "HEAD") { - # when CI checks out just the single commit, https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml - $sourceBranch = $env:BUILD_SOURCEBRANCH -replace "^refs/heads/" -} - -if ([string]::IsNullOrWhiteSpace($branch)) { - throw "Branch is null or empty!" -} - -if ([string]::IsNullOrWhiteSpace($sourceBranch)) { - throw "SourceBranch is null or empty!" -} - -Write-Host "Branch is $branch" -Write-Host "SourceBranch is $sourceBranch" - -$discard = @( - "^Update dependencies from https:\/\/", - "^\[.+\] Update dependencies from", - "^LEGO: Pull request from lego", - "^Localized file check-in by OneLocBuild Task:", - "^Juno: check in to lego" -) -join "|" - -$prUrl = "$repoUrl/pull/" -$tagVersionNumber = $tag -replace '^v' -# using .. because I want to know the changes that are on this branch, but don't care about the changes that I don't have https://stackoverflow.com/a/24186641/3065397 -$log = (git -C $Path log "$start..$end" --oneline --pretty="format:%s" --first-parent) -$date = ([datetime](git -C $path log -1 --format=%ai $end)).ToString("MMMM yyyy") -$issues = $log | ForEach-Object { - if ($_ -notmatch $discard) { - if ($_ -match '^(?.+)\s\(#(?\d+)\)?$') { - $message = "* $($matches.message)" - if ($matches.pr) { - $pr = $matches.pr - $message += " [#$pr]($prUrl$pr)" - } - - $message - } - else { - "* $_" - } - } -} - -$output = @" - -See the release notes [here](https://github.com/microsoft/testfx/blob/main/docs/releases.md#$(("$tagVersionNumber $date" -replace "\.", "" -replace "\W", "-").ToLowerInvariant())). - -------------------------------- - -## $tagVersionNumber ($date) - -$($issues -join "`n") - -See full log [here]($repoUrl/compare/$start...$tag) - -### Artifacts - -* MSTest: [$tagVersionNumber](https://www.nuget.org/packages/MSTest/$tagVersionNumber) -* MSTest.TestFramework: [$tagVersionNumber](https://www.nuget.org/packages/MSTest.TestFramework/$tagVersionNumber) -* MSTest.TestAdapter: [$tagVersionNumber](https://www.nuget.org/packages/MSTest.TestAdapter/$tagVersionNumber) - -"@ - -$output -$output | clip diff --git a/global.json b/global.json index 7d17fddb6b..8ebaf21ce6 100644 --- a/global.json +++ b/global.json @@ -4,9 +4,9 @@ "runtimes": { "dotnet": [ "3.1.32", - "6.0.29", - "7.0.18", - "8.0.4" + "6.0.32", + "7.0.20", + "8.0.7" ] }, "vs": { @@ -19,7 +19,7 @@ "rollForward": "latestFeature" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24225.1", + "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24360.5", "MSBuild.Sdk.Extras": "3.0.44" } } diff --git a/samples/DemoMSTestSdk/Directory.Build.props b/samples/DemoMSTestSdk/Directory.Build.props deleted file mode 100644 index 8c119d5413..0000000000 --- a/samples/DemoMSTestSdk/Directory.Build.props +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/samples/DemoMSTestSdk/ProjectUsingMSTestRunner/Usings.cs b/samples/DemoMSTestSdk/ProjectUsingMSTestRunner/Usings.cs deleted file mode 100644 index af4ead20be..0000000000 --- a/samples/DemoMSTestSdk/ProjectUsingMSTestRunner/Usings.cs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -global using Microsoft.VisualStudio.TestTools.UnitTesting; \ No newline at end of file diff --git a/samples/DemoMSTestSdk/ProjectUsingVSTest/Usings.cs b/samples/DemoMSTestSdk/ProjectUsingVSTest/Usings.cs deleted file mode 100644 index af4ead20be..0000000000 --- a/samples/DemoMSTestSdk/ProjectUsingVSTest/Usings.cs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -global using Microsoft.VisualStudio.TestTools.UnitTesting; \ No newline at end of file diff --git a/samples/DemoMSTestSdk/ProjectWithNativeAOT/Usings.cs b/samples/DemoMSTestSdk/ProjectWithNativeAOT/Usings.cs deleted file mode 100644 index af4ead20be..0000000000 --- a/samples/DemoMSTestSdk/ProjectWithNativeAOT/Usings.cs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -global using Microsoft.VisualStudio.TestTools.UnitTesting; \ No newline at end of file diff --git a/samples/Playground/Playground.csproj b/samples/Playground/Playground.csproj index 81fcb3d44e..57236d10af 100644 --- a/samples/Playground/Playground.csproj +++ b/samples/Playground/Playground.csproj @@ -1,4 +1,4 @@ - + Exe @@ -16,7 +16,6 @@ - diff --git a/samples/Playground/Program.cs b/samples/Playground/Program.cs index a29b811b5e..1a5cf88f8a 100644 --- a/samples/Playground/Program.cs +++ b/samples/Playground/Program.cs @@ -16,7 +16,7 @@ public static async Task Main(string[] args) Environment.SetEnvironmentVariable("DOTNET_CLI_TELEMETRY_OPTOUT", "1"); ITestApplicationBuilder testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); - testApplicationBuilder.AddMSTest(() => new[] { Assembly.GetEntryAssembly()! }); + testApplicationBuilder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); // Enable Trx // testApplicationBuilder.AddTrxReportProvider(); diff --git a/samples/mstest-runner/Directory.Build.props b/samples/mstest-runner/Directory.Build.props deleted file mode 100644 index 8c119d5413..0000000000 --- a/samples/mstest-runner/Directory.Build.props +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/samples/mstest-runner/Directory.Build.targets b/samples/mstest-runner/Directory.Build.targets deleted file mode 100644 index 8c119d5413..0000000000 --- a/samples/mstest-runner/Directory.Build.targets +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/samples/mstest-runner/Directory.Packages.props b/samples/mstest-runner/Directory.Packages.props deleted file mode 100644 index 36a2328785..0000000000 --- a/samples/mstest-runner/Directory.Packages.props +++ /dev/null @@ -1,6 +0,0 @@ - - - - false - - diff --git a/samples/mstest-runner/NuGet.config b/samples/mstest-runner/NuGet.config deleted file mode 100644 index 20182a5787..0000000000 --- a/samples/mstest-runner/NuGet.config +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/samples/DemoMSTestSdk/DemoMSTestSdk.sln b/samples/public/DemoMSTestSdk/DemoMSTestSdk.sln similarity index 64% rename from samples/DemoMSTestSdk/DemoMSTestSdk.sln rename to samples/public/DemoMSTestSdk/DemoMSTestSdk.sln index 7569e194de..34e4049f55 100644 --- a/samples/DemoMSTestSdk/DemoMSTestSdk.sln +++ b/samples/public/DemoMSTestSdk/DemoMSTestSdk.sln @@ -3,11 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.11.34807.42 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectUsingVSTest", "ProjectUsingVSTest\ProjectUsingVSTest.csproj", "{0360A072-0ED6-4A27-9AE5-3C6DD055733E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectUsingVSTest", "ProjectUsingVSTest\ProjectUsingVSTest.csproj", "{0360A072-0ED6-4A27-9AE5-3C6DD055733E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectUsingMSTestRunner", "ProjectUsingMSTestRunner\ProjectUsingMSTestRunner.csproj", "{8DA3F43B-938F-4A1E-BE58-B7F80386187F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectUsingMSTestRunner", "ProjectUsingMSTestRunner\ProjectUsingMSTestRunner.csproj", "{8DA3F43B-938F-4A1E-BE58-B7F80386187F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectWithNativeAOT", "ProjectWithNativeAOT\ProjectWithNativeAOT.csproj", "{9EA03738-C4AA-4CD7-BF71-BA8E689522D7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectWithNativeAOT", "ProjectWithNativeAOT\ProjectWithNativeAOT.csproj", "{9EA03738-C4AA-4CD7-BF71-BA8E689522D7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectUsingPlaywright", "ProjectUsingPlaywright\ProjectUsingPlaywright.csproj", "{D047E30F-BEC1-496A-A84B-D27E0966F7DD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectUsingAspire", "ProjectUsingAspire\ProjectUsingAspire.csproj", "{274001A3-B33E-4235-92D4-1A8F39595F5F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,6 +31,14 @@ Global {9EA03738-C4AA-4CD7-BF71-BA8E689522D7}.Debug|Any CPU.Build.0 = Debug|Any CPU {9EA03738-C4AA-4CD7-BF71-BA8E689522D7}.Release|Any CPU.ActiveCfg = Release|Any CPU {9EA03738-C4AA-4CD7-BF71-BA8E689522D7}.Release|Any CPU.Build.0 = Release|Any CPU + {D047E30F-BEC1-496A-A84B-D27E0966F7DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D047E30F-BEC1-496A-A84B-D27E0966F7DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D047E30F-BEC1-496A-A84B-D27E0966F7DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D047E30F-BEC1-496A-A84B-D27E0966F7DD}.Release|Any CPU.Build.0 = Release|Any CPU + {274001A3-B33E-4235-92D4-1A8F39595F5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {274001A3-B33E-4235-92D4-1A8F39595F5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {274001A3-B33E-4235-92D4-1A8F39595F5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {274001A3-B33E-4235-92D4-1A8F39595F5F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/public/DemoMSTestSdk/ProjectUsingAspire/IntegrationTest1.cs b/samples/public/DemoMSTestSdk/ProjectUsingAspire/IntegrationTest1.cs new file mode 100644 index 0000000000..ebe1d15e63 --- /dev/null +++ b/samples/public/DemoMSTestSdk/ProjectUsingAspire/IntegrationTest1.cs @@ -0,0 +1,30 @@ +namespace Aspire.Tests1; + +[TestClass] +public class IntegrationTest1 +{ + // Instructions: + // 1. Add a project reference to the target AppHost project, e.g.: + // + // + // + // + // + // 2. Uncomment the following example test and update 'Projects.MyAspireApp_AppHost' to match your AppHost project: + // + // [TestMethod] + // public async Task GetWebResourceRootReturnsOkStatusCode() + // { + // // Arrange + // var appHost = await DistributedApplicationTestingBuilder.CreateAsync(); + // await using var app = await appHost.BuildAsync(); + // await app.StartAsync(); + + // // Act + // var httpClient = app.CreateHttpClient("webfrontend"); + // var response = await httpClient.GetAsync("/"); + + // // Assert + // Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + // } +} diff --git a/samples/public/DemoMSTestSdk/ProjectUsingAspire/ProjectUsingAspire.csproj b/samples/public/DemoMSTestSdk/ProjectUsingAspire/ProjectUsingAspire.csproj new file mode 100644 index 0000000000..37f2cb0dab --- /dev/null +++ b/samples/public/DemoMSTestSdk/ProjectUsingAspire/ProjectUsingAspire.csproj @@ -0,0 +1,44 @@ + + + + net8.0 + enable + enable + false + + true + + + + + \ No newline at end of file diff --git a/samples/DemoMSTestSdk/ProjectUsingMSTestRunner/ProjectUsingMSTestRunner.csproj b/samples/public/DemoMSTestSdk/ProjectUsingMSTestRunner/ProjectUsingMSTestRunner.csproj similarity index 65% rename from samples/DemoMSTestSdk/ProjectUsingMSTestRunner/ProjectUsingMSTestRunner.csproj rename to samples/public/DemoMSTestSdk/ProjectUsingMSTestRunner/ProjectUsingMSTestRunner.csproj index cf3755359b..18c4574c0d 100644 --- a/samples/DemoMSTestSdk/ProjectUsingMSTestRunner/ProjectUsingMSTestRunner.csproj +++ b/samples/public/DemoMSTestSdk/ProjectUsingMSTestRunner/ProjectUsingMSTestRunner.csproj @@ -1,7 +1,7 @@ - + @@ -30,12 +30,16 @@ Below is the equivalent project configuration when not using MSTest.Sdk - - - - - - + + + + + + + + + + diff --git a/samples/DemoMSTestSdk/ProjectUsingMSTestRunner/UnitTest1.cs b/samples/public/DemoMSTestSdk/ProjectUsingMSTestRunner/UnitTest1.cs similarity index 100% rename from samples/DemoMSTestSdk/ProjectUsingMSTestRunner/UnitTest1.cs rename to samples/public/DemoMSTestSdk/ProjectUsingMSTestRunner/UnitTest1.cs diff --git a/samples/public/DemoMSTestSdk/ProjectUsingPlaywright/ProjectUsingPlaywright.csproj b/samples/public/DemoMSTestSdk/ProjectUsingPlaywright/ProjectUsingPlaywright.csproj new file mode 100644 index 0000000000..0f40510be0 --- /dev/null +++ b/samples/public/DemoMSTestSdk/ProjectUsingPlaywright/ProjectUsingPlaywright.csproj @@ -0,0 +1,45 @@ + + + + net8.0 + enable + enable + false + + true + + + + + + diff --git a/samples/public/DemoMSTestSdk/ProjectUsingPlaywright/UnitTest1.cs b/samples/public/DemoMSTestSdk/ProjectUsingPlaywright/UnitTest1.cs new file mode 100644 index 0000000000..8fc7eb00e1 --- /dev/null +++ b/samples/public/DemoMSTestSdk/ProjectUsingPlaywright/UnitTest1.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Text.RegularExpressions; + +using Microsoft.Playwright; + +namespace ProjectUsingPlaywright; + +[TestClass] +public class UnitTest1 : PageTest +{ + [TestMethod] + public async Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingToTheIntroPage() + { + await Page.GotoAsync("https://playwright.dev"); + + // Expect a title "to contain" a substring. + await Expect(Page).ToHaveTitleAsync(new Regex("Playwright")); + + // create a locator + ILocator getStarted = Page.Locator("text=Get Started"); + + // Expect an attribute "to be strictly equal" to the value. + await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/intro"); + + // Click the get started link. + await getStarted.ClickAsync(); + + // Expects the URL to contain intro. + await Expect(Page).ToHaveURLAsync(new Regex(".*intro")); + } +} diff --git a/samples/DemoMSTestSdk/ProjectUsingVSTest/ProjectUsingVSTest.csproj b/samples/public/DemoMSTestSdk/ProjectUsingVSTest/ProjectUsingVSTest.csproj similarity index 54% rename from samples/DemoMSTestSdk/ProjectUsingVSTest/ProjectUsingVSTest.csproj rename to samples/public/DemoMSTestSdk/ProjectUsingVSTest/ProjectUsingVSTest.csproj index 4d54735e5f..4b91c6c917 100644 --- a/samples/DemoMSTestSdk/ProjectUsingVSTest/ProjectUsingVSTest.csproj +++ b/samples/public/DemoMSTestSdk/ProjectUsingVSTest/ProjectUsingVSTest.csproj @@ -1,7 +1,7 @@ - + @@ -29,7 +29,16 @@ Below is the equivalent project configuration when not using MSTest.Sdk - + + + + + + + + + + diff --git a/samples/DemoMSTestSdk/ProjectUsingVSTest/UnitTest1.cs b/samples/public/DemoMSTestSdk/ProjectUsingVSTest/UnitTest1.cs similarity index 100% rename from samples/DemoMSTestSdk/ProjectUsingVSTest/UnitTest1.cs rename to samples/public/DemoMSTestSdk/ProjectUsingVSTest/UnitTest1.cs diff --git a/samples/DemoMSTestSdk/ProjectWithNativeAOT/ProjectWithNativeAOT.csproj b/samples/public/DemoMSTestSdk/ProjectWithNativeAOT/ProjectWithNativeAOT.csproj similarity index 66% rename from samples/DemoMSTestSdk/ProjectWithNativeAOT/ProjectWithNativeAOT.csproj rename to samples/public/DemoMSTestSdk/ProjectWithNativeAOT/ProjectWithNativeAOT.csproj index 9cb018db53..cdb413b816 100644 --- a/samples/DemoMSTestSdk/ProjectWithNativeAOT/ProjectWithNativeAOT.csproj +++ b/samples/public/DemoMSTestSdk/ProjectWithNativeAOT/ProjectWithNativeAOT.csproj @@ -1,7 +1,7 @@ - + @@ -31,13 +31,18 @@ Below is the equivalent project configuration when not using MSTest.Sdk - - - - - - - + + + + + + + + + + + + diff --git a/samples/DemoMSTestSdk/ProjectWithNativeAOT/UnitTest1.cs b/samples/public/DemoMSTestSdk/ProjectWithNativeAOT/UnitTest1.cs similarity index 100% rename from samples/DemoMSTestSdk/ProjectWithNativeAOT/UnitTest1.cs rename to samples/public/DemoMSTestSdk/ProjectWithNativeAOT/UnitTest1.cs diff --git a/samples/public/Directory.Build.props b/samples/public/Directory.Build.props new file mode 100644 index 0000000000..1a0be96653 --- /dev/null +++ b/samples/public/Directory.Build.props @@ -0,0 +1,13 @@ + + + + 8.0.1 + 17.11.3 + 3.4.3 + 1.0.0-alpha.24325.1 + 1.44.0 + 1.2.1 + 17.10.0 + + + diff --git a/samples/DemoMSTestSdk/Directory.Build.targets b/samples/public/Directory.Build.targets similarity index 100% rename from samples/DemoMSTestSdk/Directory.Build.targets rename to samples/public/Directory.Build.targets diff --git a/samples/DemoMSTestSdk/Directory.Packages.props b/samples/public/Directory.Packages.props similarity index 100% rename from samples/DemoMSTestSdk/Directory.Packages.props rename to samples/public/Directory.Packages.props diff --git a/samples/DemoMSTestSdk/NuGet.config b/samples/public/NuGet.config similarity index 100% rename from samples/DemoMSTestSdk/NuGet.config rename to samples/public/NuGet.config diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/In-process extensions/CompositeExtensionFactorySample.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/In-process extensions/CompositeExtensionFactorySample.cs new file mode 100644 index 0000000000..ae0d59ef39 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/In-process extensions/CompositeExtensionFactorySample.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.TestHost; + +namespace TestingPlatformExplorer.InProcess; + +internal class DisplayCompositeExtensionFactorySample : ITestSessionLifetimeHandler, IDataConsumer, IOutputDeviceDataProducer +{ + private readonly IOutputDevice _outputDevice; + private int _testNodeUpdateMessageCount; + + public Type[] DataTypesConsumed => new[] { typeof(TestNodeUpdateMessage) }; + + public string Uid => nameof(DisplayCompositeExtensionFactorySample); + + public string Version => "1.0.0"; + + public string DisplayName => nameof(DisplayCompositeExtensionFactorySample); + + public string Description => ""; + + public DisplayCompositeExtensionFactorySample(IOutputDevice outputDevice) + { + _outputDevice = outputDevice; + } + + public Task IsEnabledAsync() => Task.FromResult(true); + + public async Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) + { + _testNodeUpdateMessageCount++; + var testNodeUpdateMessage = (TestNodeUpdateMessage)value; + string testNodeDisplayName = testNodeUpdateMessage.TestNode.DisplayName; + TestNodeUid testNodeId = testNodeUpdateMessage.TestNode.Uid; + + TestNodeStateProperty nodeState = testNodeUpdateMessage.TestNode.Properties.Single(); + + switch (nodeState) + { + case InProgressTestNodeStateProperty _: + { + await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"[DisplayCompositeExtensionFactorySample]TestNode '{testNodeId}' with display name '{testNodeDisplayName}' is in progress") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } + }); + break; + } + case PassedTestNodeStateProperty _: + { + await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"[DisplayCompositeExtensionFactorySample]TestNode '{testNodeId}' with display name '{testNodeDisplayName}' is completed") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } + }); + break; + } + case FailedTestNodeStateProperty failedTestNodeStateProperty: + { + await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"[DisplayCompositeExtensionFactorySample]TestNode '{testNodeId}' with display name '{testNodeDisplayName}' is failed with '{failedTestNodeStateProperty?.Exception?.Message}'") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Red } + }); + break; + } + case SkippedTestNodeStateProperty _: + { + await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"[DisplayCompositeExtensionFactorySample]TestNode '{testNodeId}' with display name '{testNodeDisplayName}' is skipped") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.White } + }); + break; + } + default: + break; + } + } + + public async Task OnTestSessionStartingAsync(SessionUid sessionUid, CancellationToken cancellationToken) + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData("[DisplayCompositeExtensionFactorySample]Hello from OnTestSessionStartingAsync") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.DarkGreen } + }); + + public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, CancellationToken cancellationToken) + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"[DisplayCompositeExtensionFactorySample]Total received 'TestNodeUpdateMessage': {_testNodeUpdateMessageCount}") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } + }); +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/In-process extensions/DisplayDataConsumer.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/In-process extensions/DisplayDataConsumer.cs new file mode 100644 index 0000000000..b8ef1188c3 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/In-process extensions/DisplayDataConsumer.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.OutputDevice; + +namespace TestingPlatformExplorer.InProcess; +internal class DisplayDataConsumer : IDataConsumer, IOutputDeviceDataProducer +{ + private readonly IOutputDevice _outputDevice; + + public Type[] DataTypesConsumed => new[] { typeof(TestNodeUpdateMessage) }; + + public string Uid => nameof(DisplayDataConsumer); + + public string Version => "1.0.0"; + + public string DisplayName => nameof(DisplayDataConsumer); + + public string Description => "This extension display in console the testnode id and display name of TestNodeUpdateMessage data type."; + + public DisplayDataConsumer(IOutputDevice outputDevice) + { + _outputDevice = outputDevice; + } + + public async Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) + { + var testNodeUpdateMessage = (TestNodeUpdateMessage)value; + string testNodeDisplayName = testNodeUpdateMessage.TestNode.DisplayName; + TestNodeUid testNodeId = testNodeUpdateMessage.TestNode.Uid; + + TestNodeStateProperty nodeState = testNodeUpdateMessage.TestNode.Properties.Single(); + + switch (nodeState) + { + case InProgressTestNodeStateProperty _: + { + await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"[DisplayDataConsumer]TestNode '{testNodeId}' with display name '{testNodeDisplayName}' is in progress") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } + }); + break; + } + case PassedTestNodeStateProperty _: + { + await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"[DisplayDataConsumer]TestNode '{testNodeId}' with display name '{testNodeDisplayName}' is completed") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } + }); + break; + } + case FailedTestNodeStateProperty failedTestNodeStateProperty: + { + await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"[DisplayDataConsumer]TestNode '{testNodeId}' with display name '{testNodeDisplayName}' is failed with '{failedTestNodeStateProperty?.Exception?.Message}'") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Red } + }); + break; + } + case SkippedTestNodeStateProperty _: + { + await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"[DisplayDataConsumer]TestNode '{testNodeId}' with display name '{testNodeDisplayName}' is skipped") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.White } + }); + break; + } + default: + break; + } + } + public Task IsEnabledAsync() => Task.FromResult(true); +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/In-process extensions/DisplayTestApplicationLifecycleCallbacks.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/In-process extensions/DisplayTestApplicationLifecycleCallbacks.cs new file mode 100644 index 0000000000..20776d3148 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/In-process extensions/DisplayTestApplicationLifecycleCallbacks.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.OutputDevice; + +namespace TestingPlatformExplorer.InProcess; +internal class DisplayTestApplicationLifecycleCallbacks : ITestApplicationLifecycleCallbacks, IOutputDeviceDataProducer +{ + private readonly IOutputDevice _outputDevice; + + public string Uid => nameof(DisplayTestApplicationLifecycleCallbacks); + + public string Version => "1.0.0"; + + public string DisplayName => nameof(DisplayTestApplicationLifecycleCallbacks); + + public string Description => "This extension display in console the before/after run"; + + public DisplayTestApplicationLifecycleCallbacks(IOutputDevice outputDevice) + { + _outputDevice = outputDevice; + } + + public async Task AfterRunAsync(int exitCode, CancellationToken cancellation) + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"Hello from AfterRunAsync, exit code: {exitCode}") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.DarkGreen } + }); + + public async Task BeforeRunAsync(CancellationToken cancellationToken) + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData("Hello from BeforeRunAsync") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.DarkGreen } + }); + + public Task IsEnabledAsync() => Task.FromResult(true); +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/In-process extensions/DisplayTestSessionLifeTimeHandler.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/In-process extensions/DisplayTestSessionLifeTimeHandler.cs new file mode 100644 index 0000000000..230a3436d8 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/In-process extensions/DisplayTestSessionLifeTimeHandler.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.TestHost; + +namespace TestingPlatformExplorer.InProcess; +internal class DisplayTestSessionLifeTimeHandler : ITestSessionLifetimeHandler, + IOutputDeviceDataProducer, + IAsyncInitializableExtension, + IAsyncCleanableExtension, + IAsyncDisposable +{ + private readonly IOutputDevice _outputDevice; + + public string Uid => "This extension display in console the session start/end"; + + public string Version => "1.0.0"; + + public string DisplayName => nameof(DisplayTestSessionLifeTimeHandler); + + public string Description => "This extension display in console the session start/end"; + + public DisplayTestSessionLifeTimeHandler(IOutputDevice outputDevice) + { + _outputDevice = outputDevice; + } + + public Task IsEnabledAsync() => Task.FromResult(true); + + public async Task OnTestSessionStartingAsync(SessionUid sessionUid, CancellationToken cancellationToken) + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData("Hello from OnTestSessionStartingAsync") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.DarkGreen } + }); + + public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, CancellationToken cancellationToken) + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData("Hello from OnTestSessionFinishingAsync") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.DarkGreen } + }); + public async Task InitializeAsync() + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData("Hello from InitializeAsync") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.DarkGreen } + }); + + public async Task CleanupAsync() + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData("Hello from CleanupAsync") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.DarkGreen } + }); + + public async ValueTask DisposeAsync() + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData("Hello from DisposeAsync") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.DarkGreen } + }); +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/Out-of-process extensions/MonitorTestHost.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/Out-of-process extensions/MonitorTestHost.cs new file mode 100644 index 0000000000..a685152567 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/Out-of-process extensions/MonitorTestHost.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestHostControllers; +using Microsoft.Testing.Platform.OutputDevice; + +namespace TestingPlatformExplorer.OutOfProcess; + +internal class MonitorTestHost : ITestHostProcessLifetimeHandler, IOutputDeviceDataProducer +{ + private readonly IOutputDevice _outputDevice; + + public MonitorTestHost(IOutputDevice outputDevice) + { + _outputDevice = outputDevice; + } + + public string Uid => nameof(MonitorTestHost); + + public string Version => "1.0.0"; + + public string DisplayName => nameof(MonitorTestHost); + + public string Description => "Example of monitoring the test host process."; + + public Task IsEnabledAsync() => Task.FromResult(true); + + public async Task BeforeTestHostProcessStartAsync(CancellationToken cancellationToken) + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData("BeforeTestHostProcessStartAsync") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } + }); + + public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testHostProcessInformation, CancellationToken cancellation) + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"OnTestHostProcessExitedAsync, test host exited with exit code {testHostProcessInformation.ExitCode}") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } + }); + + public async Task OnTestHostProcessStartedAsync(ITestHostProcessInformation testHostProcessInformation, CancellationToken cancellation) + => await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"OnTestHostProcessStartedAsync, test host started with PID {testHostProcessInformation.PID}") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } + }); +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/Out-of-process extensions/SetEnvironmentVariableForTestHost.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/Out-of-process extensions/SetEnvironmentVariableForTestHost.cs new file mode 100644 index 0000000000..b3b0fb7160 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/Out-of-process extensions/SetEnvironmentVariableForTestHost.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.TestHostControllers; + +namespace TestingPlatformExplorer.OutOfProcess; + +internal class SetEnvironmentVariableForTestHost : ITestHostEnvironmentVariableProvider +{ + public string Uid => nameof(SetEnvironmentVariableForTestHost); + + public string Version => "1.0.0"; + + public string DisplayName => nameof(SetEnvironmentVariableForTestHost); + + public string Description => "Example of setting environment variables for the test host."; + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task UpdateAsync(IEnvironmentVariables environmentVariables) + { + environmentVariables.SetVariable(new EnvironmentVariable("SAMPLE", "SAMPLE_VALUE", false, true)); + return Task.CompletedTask; + } + + public Task ValidateTestHostEnvironmentVariablesAsync(IReadOnlyEnvironmentVariables environmentVariables) + => environmentVariables.TryGetVariable("SAMPLE", out OwnedEnvironmentVariable? value) && value.Value == "SAMPLE_VALUE" + ? ValidationResult.ValidTask + : ValidationResult.InvalidTask("The environment variable 'SAMPLE' is not set to 'SAMPLE_VALUE'."); +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/Program.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/Program.cs new file mode 100644 index 0000000000..441c6d7cfd --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/Program.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Reflection; + +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Services; + +using TestingPlatformExplorer.InProcess; +using TestingPlatformExplorer.OutOfProcess; +using TestingPlatformExplorer.TestingFramework; + +// Create the test application builder +ITestApplicationBuilder testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +// Register the testing framework +testApplicationBuilder.AddTestingFramework(() => new[] { Assembly.GetExecutingAssembly() }); + +// In-process & out-of-process extensions +// Register the testing framework command line options +testApplicationBuilder.CommandLine.AddProvider(() => new TestingFrameworkCommandLineOptions()); + +// In-process extensions +testApplicationBuilder.TestHost.AddTestApplicationLifecycleCallbacks(serviceProvider + => new DisplayTestApplicationLifecycleCallbacks(serviceProvider.GetOutputDevice())); +testApplicationBuilder.TestHost.AddTestSessionLifetimeHandle(serviceProvider + => new DisplayTestSessionLifeTimeHandler(serviceProvider.GetOutputDevice())); +testApplicationBuilder.TestHost.AddDataConsumer(serviceProvider + => new DisplayDataConsumer(serviceProvider.GetOutputDevice())); + +// Out-of-process extensions +testApplicationBuilder.TestHostControllers.AddEnvironmentVariableProvider(_ + => new SetEnvironmentVariableForTestHost()); +testApplicationBuilder.TestHostControllers.AddProcessLifetimeHandler(serviceProvider => + new MonitorTestHost(serviceProvider.GetOutputDevice())); + +// In-process composite extension SessionLifeTimeHandler+DataConsumer +CompositeExtensionFactory compositeExtensionFactory = new(serviceProvider => new DisplayCompositeExtensionFactorySample(serviceProvider.GetOutputDevice())); +testApplicationBuilder.TestHost.AddTestSessionLifetimeHandle(compositeExtensionFactory); +testApplicationBuilder.TestHost.AddDataConsumer(compositeExtensionFactory); + +using ITestApplication testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/Properties/launchSettings.json b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/Properties/launchSettings.json new file mode 100644 index 0000000000..696dc6823e --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "TestingPlatformExplorer": { + "commandName": "Project", + "commandLineArgs": "--dop 1 --generatereport --reportfilename testframeworkreport.txt --diagnostic", + "environmentVariables": { "TestingFramework__DisableParallelism": "True" } + } + } +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/AssertionException.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/AssertionException.cs new file mode 100644 index 0000000000..a42aab42e2 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/AssertionException.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace TestingPlatformExplorer.TestingFramework; + +[Serializable] +public class AssertionException : Exception +{ + public AssertionException() + { + } + + public AssertionException(string? message) + : base(message) + { + } + + public AssertionException(string? message, Exception? innerException) + : base(message, innerException) + { + } +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.Assertions.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.Assertions.cs new file mode 100644 index 0000000000..5330e13656 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.Assertions.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace TestingPlatformExplorer.TestingFramework; + +public static class Assert +{ + public static void AreEqual(T expected, T actual) + { + if (expected is null) + { + throw new ArgumentException("'expected' cannot be null", nameof(expected)); + } + + if (actual is null) + { + throw new ArgumentException("'actual' cannot be null", nameof(actual)); + } + + if (!expected.Equals(actual)) + { + throw new AssertionException($"Expected: {expected}, Actual: {actual}"); + } + } +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.Attributes.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.Attributes.cs new file mode 100644 index 0000000000..c1c83b63a1 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.Attributes.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace TestingPlatformExplorer.TestingFramework; + +[AttributeUsage(AttributeTargets.Method)] +public class SkipAttribute : Attribute +{ +} + +[AttributeUsage(AttributeTargets.Method)] +public class TestMethodAttribute : Attribute +{ +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.CommandLineOptions.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.CommandLineOptions.cs new file mode 100644 index 0000000000..788efe5c75 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.CommandLineOptions.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; + +namespace TestingPlatformExplorer.TestingFramework; + +internal sealed class TestingFrameworkCommandLineOptions : ICommandLineOptionsProvider +{ + public const string DopOption = "dop"; + public const string GenerateReportOption = "generatereport"; + public const string ReportFilenameOption = "reportfilename"; + + public string Uid => nameof(TestingFrameworkCommandLineOptions); + + public string Version => "1.0.0"; + + public string DisplayName => nameof(TestingFrameworkCommandLineOptions); + + public string Description => "Testing framework command line options"; + + public IReadOnlyCollection GetCommandLineOptions() => new[] + { + new CommandLineOption(DopOption,"Degree of parallelism", ArgumentArity.ExactlyOne, false), + new CommandLineOption(GenerateReportOption,"Generate a test report file", ArgumentArity.Zero, false), + new CommandLineOption(ReportFilenameOption,"Report file name to use together with --generatereport", ArgumentArity.ExactlyOne, false), + }; + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) + { + if (commandOption.Name == DopOption) + { + if (!int.TryParse(arguments[0], out int dopValue) || dopValue <= 0) + { + return ValidationResult.InvalidTask("Dop must be a positive integer"); + } + } + + return ValidationResult.ValidTask; + } + + public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) + { + bool generateReportEnabled = commandLineOptions.IsOptionSet(GenerateReportOption); + bool reportFileName = commandLineOptions.TryGetOptionArgumentList(ReportFilenameOption, out string[]? _); + + return (generateReportEnabled || reportFileName) && !(generateReportEnabled && reportFileName) + ? ValidationResult.InvalidTask("--generatereport and --reportfilename must be specified together") + : ValidationResult.ValidTask; + } +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.Registration.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.Registration.cs new file mode 100644 index 0000000000..5d2c2ddb9a --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.Registration.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Reflection; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Services; + +namespace TestingPlatformExplorer.TestingFramework; + +public static class TestingFrameworkExtensions +{ + public static void AddTestingFramework(this ITestApplicationBuilder builder, Func assemblies) + => builder.RegisterTestFramework(_ => new TestingFrameworkCapabilities(), + (capabilities, serviceProvider) => new TestingFramework(capabilities, + serviceProvider.GetCommandLineOptions(), + serviceProvider.GetConfiguration(), + serviceProvider.GetLoggerFactory().CreateLogger(), + serviceProvider.GetOutputDevice(), + assemblies)); +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.cs new file mode 100644 index 0000000000..0176a0c973 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.cs @@ -0,0 +1,249 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; +using System.Reflection; +using System.Text; + +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Requests; + +namespace TestingPlatformExplorer.TestingFramework; + +internal sealed class TestingFramework : ITestFramework, IDataProducer, IDisposable, IOutputDeviceDataProducer +{ + private readonly TestingFrameworkCapabilities _capabilities; + private readonly ICommandLineOptions _commandLineOptions; + private readonly IConfiguration _configuration; + private readonly ILogger _logger; + private readonly IOutputDevice _outputDevice; + private readonly Assembly[] _assemblies; + private readonly SemaphoreSlim _dop; + private readonly string _reportFile = string.Empty; + private readonly int _dopValue; + + public TestingFramework( + ITestFrameworkCapabilities capabilities, + ICommandLineOptions commandLineOptions, + IConfiguration configuration, + ILogger logger, + IOutputDevice outputDevice, + Func assemblies) + { + _capabilities = (TestingFrameworkCapabilities)capabilities; + _commandLineOptions = commandLineOptions; + _configuration = configuration; + _logger = logger; + _outputDevice = outputDevice; + _assemblies = assemblies(); + + if (_commandLineOptions.TryGetOptionArgumentList(TestingFrameworkCommandLineOptions.DopOption, out string[]? argumentList)) + { + _dopValue = int.Parse(argumentList[0], CultureInfo.InvariantCulture); + _dop = new SemaphoreSlim(_dopValue, _dopValue); + } + else + { + _dop = new SemaphoreSlim(int.MaxValue, int.MaxValue); + } + + if (_configuration["TestingFramework:DisableParallelism"] == bool.TrueString) + { + _dop?.Dispose(); + _dop = new SemaphoreSlim(1, 1); + _dopValue = 1; + } + + if (_commandLineOptions.IsOptionSet(TestingFrameworkCommandLineOptions.GenerateReportOption)) + { + if (_commandLineOptions.TryGetOptionArgumentList(TestingFrameworkCommandLineOptions.ReportFilenameOption, out string[]? reportFile)) + { + _reportFile = reportFile[0]; + } + } + } + + public string Uid => nameof(TestingFramework); + + public string Version => "1.0.0"; + + public string DisplayName => "TestingFramework"; + + public string Description => "Testing framework sample"; + + public Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage), typeof(SessionFileArtifact) }; + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + if (_logger.IsEnabled(LogLevel.Debug)) + { + await _logger.LogDebugAsync($"Executing request of type '{context.Request}'"); + } + + switch (context.Request) + { + case DiscoverTestExecutionRequest discoverTestExecutionRequest: + { + try + { + MethodInfo[] tests = GetTestsMethodFromAssemblies(); + foreach (MethodInfo test in tests) + { + var testNode = new TestNode() + { + Uid = $"{test.DeclaringType!.FullName}.{test.Name}", + DisplayName = test.Name, + Properties = new PropertyBag(DiscoveredTestNodeStateProperty.CachedInstance), + }; + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(discoverTestExecutionRequest.Session.SessionUid, testNode)); + } + } + finally + { + // Ensure to complete the request also in case of exception + context.Complete(); + } + + break; + } + + case RunTestExecutionRequest runTestExecutionRequest: + { + try + { + await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"TestingFramework version '{Version}' running tests with parallelism of {_dopValue}") { ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } }); + + StringBuilder reportBody = new(); + MethodInfo[] tests = GetTestsMethodFromAssemblies(); + List results = new(); + foreach (MethodInfo test in tests) + { + if (test.GetCustomAttribute() != null) + { + var skippedTestNode = new TestNode() + { + Uid = $"{test.DeclaringType!.FullName}.{test.Name}", + DisplayName = test.Name, + Properties = new PropertyBag(SkippedTestNodeStateProperty.CachedInstance), + }; + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(runTestExecutionRequest.Session.SessionUid, skippedTestNode)); + + lock (reportBody) + { + reportBody.AppendLine(CultureInfo.InvariantCulture, $"Test {skippedTestNode.Uid} skipped"); + } + + continue; + } + + results.Add(Task.Run(async () => + { + await _dop.WaitAsync(); + try + { + object? instance = Activator.CreateInstance(test.DeclaringType!); + try + { + test.Invoke(instance, null); + + var successfulTestNode = new TestNode() + { + Uid = $"{test.DeclaringType!.FullName}.{test.Name}", + DisplayName = test.Name, + Properties = new PropertyBag(PassedTestNodeStateProperty.CachedInstance), + }; + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(runTestExecutionRequest.Session.SessionUid, successfulTestNode)); + + lock (reportBody) + { + reportBody.AppendLine(CultureInfo.InvariantCulture, $"Test {successfulTestNode.Uid} succeeded"); + } + } + catch (TargetInvocationException ex) when (ex.InnerException is AssertionException assertionException) + { + var assertionFailedTestNode = new TestNode() + { + Uid = $"{test.DeclaringType!.FullName}.{test.Name}", + DisplayName = test.Name, + Properties = new PropertyBag(new FailedTestNodeStateProperty(assertionException)), + }; + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(runTestExecutionRequest.Session.SessionUid, assertionFailedTestNode)); + + reportBody.AppendLine(CultureInfo.InvariantCulture, $"Test {assertionFailedTestNode.Uid} failed"); + } + catch (TargetInvocationException ex) + { + var failedTestNode = new TestNode() + { + Uid = $"{test.DeclaringType!.FullName}.{test.Name}", + DisplayName = test.Name, + Properties = new PropertyBag(new ErrorTestNodeStateProperty(ex.InnerException!)), + }; + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(runTestExecutionRequest.Session.SessionUid, failedTestNode)); + + lock (reportBody) + { + reportBody.AppendLine(CultureInfo.InvariantCulture, $"Test {failedTestNode.Uid} failed"); + } + } + } + finally + { + _dop.Release(); + } + })); + } + + await Task.WhenAll(results); + + if (!string.IsNullOrEmpty(_reportFile)) + { + File.WriteAllText(_reportFile, reportBody.ToString()); + await context.MessageBus.PublishAsync(this, new SessionFileArtifact(runTestExecutionRequest.Session.SessionUid, new FileInfo(_reportFile), "Testing framework report")); + } + } + finally + { + // Ensure to complete the request also in case of exception + context.Complete(); + } + + break; + } + + default: + throw new NotSupportedException($"Request {context.GetType()} not supported"); + } + } + + private MethodInfo[] GetTestsMethodFromAssemblies() + => _assemblies + .SelectMany(x => x.GetTypes()) + .SelectMany(x => x.GetMethods()) + .Where(x => x.GetCustomAttributes().Any()) + .ToArray(); + + public Task IsEnabledAsync() => Task.FromResult(true); + public void Dispose() + { + _dop.Dispose(); + } +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFrameworkCapabilities.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFrameworkCapabilities.cs new file mode 100644 index 0000000000..1b110c3dbd --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFrameworkCapabilities.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Capabilities.TestFramework; + +namespace TestingPlatformExplorer.TestingFramework; + +internal sealed class TestingFrameworkCapabilities : ITestFrameworkCapabilities +{ + public IReadOnlyCollection Capabilities => []; +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingPlatformExplorer.csproj b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingPlatformExplorer.csproj new file mode 100644 index 0000000000..bebb9ba07e --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingPlatformExplorer.csproj @@ -0,0 +1,22 @@ + + + + Exe + net8.0 + enable + enable + TestingPlatformExplorer + + + + + + + + + + PreserveNewest + + + + diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingPlatformExplorer.testingplatformconfig.json b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingPlatformExplorer.testingplatformconfig.json new file mode 100644 index 0000000000..193b3111e0 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingPlatformExplorer.testingplatformconfig.json @@ -0,0 +1,5 @@ +{ + "TestingFramework": { + "DisableParallelism": false + } +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformExplorer/UnitTests.cs b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/UnitTests.cs new file mode 100644 index 0000000000..5c676ccbc7 --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformExplorer/UnitTests.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using TestingPlatformExplorer.TestingFramework; + +namespace TestingPlatformExplorer.UnitTests; + +public class SomeTests +{ + [TestMethod] + public static void TestMethod1() => Assert.AreEqual(1, 1); + + [TestMethod] + public static void TestMethod2() => Assert.AreEqual(1, 2); + + [TestMethod] + public static void TestMethod3() + { + int a = 1; + int b = 0; + int c = a / b; + + Assert.AreEqual(c, 2); + } + + [Skip] + [TestMethod] + public static void TestMethod4() => Assert.AreEqual(1, 1); +} diff --git a/samples/public/TestingPlatformExamples/TestingPlatformSamples.sln b/samples/public/TestingPlatformExamples/TestingPlatformSamples.sln new file mode 100644 index 0000000000..d385c6012e --- /dev/null +++ b/samples/public/TestingPlatformExamples/TestingPlatformSamples.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestingPlatformExplorer", "TestingPlatformExplorer\TestingPlatformExplorer.csproj", "{0BC2CCB3-993F-4FEB-8BB6-E2F310C3AD0D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0BC2CCB3-993F-4FEB-8BB6-E2F310C3AD0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BC2CCB3-993F-4FEB-8BB6-E2F310C3AD0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BC2CCB3-993F-4FEB-8BB6-E2F310C3AD0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BC2CCB3-993F-4FEB-8BB6-E2F310C3AD0D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/samples/DemoMSTestSdk/global.json b/samples/public/global.json similarity index 51% rename from samples/DemoMSTestSdk/global.json rename to samples/public/global.json index 1561c043d4..766ec37273 100644 --- a/samples/DemoMSTestSdk/global.json +++ b/samples/public/global.json @@ -1,5 +1,5 @@ { "msbuild-sdks": { - "MSTest.Sdk": "3.3.1" + "MSTest.Sdk": "3.4.3" } } diff --git a/samples/mstest-runner/CustomReportExtension/CustomReportExtension.sln b/samples/public/mstest-runner/CustomReportExtension/CustomReportExtension.sln similarity index 100% rename from samples/mstest-runner/CustomReportExtension/CustomReportExtension.sln rename to samples/public/mstest-runner/CustomReportExtension/CustomReportExtension.sln diff --git a/samples/mstest-runner/CustomReportExtension/CustomReportExtension/CustomReportExtension.csproj b/samples/public/mstest-runner/CustomReportExtension/CustomReportExtension/CustomReportExtension.csproj similarity index 85% rename from samples/mstest-runner/CustomReportExtension/CustomReportExtension/CustomReportExtension.csproj rename to samples/public/mstest-runner/CustomReportExtension/CustomReportExtension/CustomReportExtension.csproj index 70f3d1322f..0df68e8798 100644 --- a/samples/mstest-runner/CustomReportExtension/CustomReportExtension/CustomReportExtension.csproj +++ b/samples/public/mstest-runner/CustomReportExtension/CustomReportExtension/CustomReportExtension.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/samples/mstest-runner/CustomReportExtension/CustomReportExtension/Program.cs b/samples/public/mstest-runner/CustomReportExtension/CustomReportExtension/Program.cs similarity index 100% rename from samples/mstest-runner/CustomReportExtension/CustomReportExtension/Program.cs rename to samples/public/mstest-runner/CustomReportExtension/CustomReportExtension/Program.cs diff --git a/samples/mstest-runner/CustomReportExtension/CustomReportExtension/TestResultConsoleLogger.cs b/samples/public/mstest-runner/CustomReportExtension/CustomReportExtension/TestResultConsoleLogger.cs similarity index 100% rename from samples/mstest-runner/CustomReportExtension/CustomReportExtension/TestResultConsoleLogger.cs rename to samples/public/mstest-runner/CustomReportExtension/CustomReportExtension/TestResultConsoleLogger.cs diff --git a/samples/mstest-runner/CustomReportExtension/CustomReportExtension/UnitTest1.cs b/samples/public/mstest-runner/CustomReportExtension/CustomReportExtension/UnitTest1.cs similarity index 100% rename from samples/mstest-runner/CustomReportExtension/CustomReportExtension/UnitTest1.cs rename to samples/public/mstest-runner/CustomReportExtension/CustomReportExtension/UnitTest1.cs diff --git a/samples/mstest-runner/EnsureTestFramework/EnsureTestFramework.sln b/samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework.sln similarity index 100% rename from samples/mstest-runner/EnsureTestFramework/EnsureTestFramework.sln rename to samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework.sln diff --git a/samples/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFramework.cs b/samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFramework.cs similarity index 100% rename from samples/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFramework.cs rename to samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFramework.cs diff --git a/samples/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFramework.csproj b/samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFramework.csproj similarity index 89% rename from samples/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFramework.csproj rename to samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFramework.csproj index df9e3a4340..56679174f2 100644 --- a/samples/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFramework.csproj +++ b/samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFramework.csproj @@ -6,7 +6,7 @@ - + diff --git a/samples/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFrameworkCapabilities.cs b/samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFrameworkCapabilities.cs similarity index 100% rename from samples/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFrameworkCapabilities.cs rename to samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFrameworkCapabilities.cs diff --git a/samples/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFrameworkExtension.cs b/samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFrameworkExtension.cs similarity index 100% rename from samples/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFrameworkExtension.cs rename to samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework/EnsureTestFrameworkExtension.cs diff --git a/samples/mstest-runner/EnsureTestFramework/EnsureTestFramework/TestApplicationBuilderExtensions.cs b/samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework/TestApplicationBuilderExtensions.cs similarity index 100% rename from samples/mstest-runner/EnsureTestFramework/EnsureTestFramework/TestApplicationBuilderExtensions.cs rename to samples/public/mstest-runner/EnsureTestFramework/EnsureTestFramework/TestApplicationBuilderExtensions.cs diff --git a/samples/mstest-runner/EnsureTestFramework/MyTestProject/MyTestProject.csproj b/samples/public/mstest-runner/EnsureTestFramework/MyTestProject/MyTestProject.csproj similarity index 100% rename from samples/mstest-runner/EnsureTestFramework/MyTestProject/MyTestProject.csproj rename to samples/public/mstest-runner/EnsureTestFramework/MyTestProject/MyTestProject.csproj diff --git a/samples/mstest-runner/EnsureTestFramework/MyTestProject/Program.cs b/samples/public/mstest-runner/EnsureTestFramework/MyTestProject/Program.cs similarity index 100% rename from samples/mstest-runner/EnsureTestFramework/MyTestProject/Program.cs rename to samples/public/mstest-runner/EnsureTestFramework/MyTestProject/Program.cs diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/MSTestRunnerWinUI.sln b/samples/public/mstest-runner/MSTestRunnerWinUI/MSTestRunnerWinUI.sln new file mode 100644 index 0000000000..eec4ac626e --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/MSTestRunnerWinUI.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33627.172 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTest", "UnitTest\UnitTest.csproj", "{E9CD5A8E-3214-46AE-AD52-6102A3987E97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Debug|ARM64.Build.0 = Debug|ARM64 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Debug|x64.ActiveCfg = Debug|x64 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Debug|x64.Build.0 = Debug|x64 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Debug|x64.Deploy.0 = Debug|x64 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Debug|x86.ActiveCfg = Debug|x86 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Debug|x86.Build.0 = Debug|x86 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Debug|x86.Deploy.0 = Debug|x86 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Release|ARM64.ActiveCfg = Release|ARM64 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Release|ARM64.Build.0 = Release|ARM64 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Release|ARM64.Deploy.0 = Release|ARM64 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Release|x64.ActiveCfg = Release|x64 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Release|x64.Build.0 = Release|x64 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Release|x64.Deploy.0 = Release|x64 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Release|x86.ActiveCfg = Release|x86 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Release|x86.Build.0 = Release|x86 + {E9CD5A8E-3214-46AE-AD52-6102A3987E97}.Release|x86.Deploy.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1FBE0D4D-1701-4F9B-8010-2460A18F36C0} + EndGlobalSection +EndGlobal diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/App.xaml b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/App.xaml new file mode 100644 index 0000000000..e0b0e782d8 --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/App.xaml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/App.xaml.cs b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/App.xaml.cs new file mode 100644 index 0000000000..cf9026f54e --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/App.xaml.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Builder; +using Microsoft.UI.Dispatching; +using Microsoft.UI.Xaml; +using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; +using System; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace UnitTest; + +/// +/// Provides application-specific behavior to supplement the default Application class. +/// +public partial class App : Application +{ + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + InitializeComponent(); + } + + /// + /// Invoked when the application is launched. + /// + /// Details about the launch request and process. + protected override +#if MSTEST_RUNNER + async +#endif + void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) + { +#if !MSTEST_RUNNER + Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.CreateDefaultUI(); +#endif + + _window = new MainWindow(); + _window.Activate(); + UITestMethodAttribute.DispatcherQueue = DispatcherQueue.GetForCurrentThread(); + + // Replace back with e.Arguments when https://github.com/microsoft/microsoft-ui-xaml/issues/3368 is fixed +#if MSTEST_RUNNER + // Ideally we would want to reuse the generated main so we don't have to manually handle all dependencies + // but this type is generated too late in the build process so we fail before. + // You can build, inspect the generated type to copy its content if you want. + // await TestingPlatformEntryPoint.Main(Environment.GetCommandLineArgs().Skip(1).ToArray()); + try + { + + string[] cliArgs = Environment.GetCommandLineArgs().Skip(1).ToArray(); + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(cliArgs); + Microsoft.Testing.Platform.MSBuild.TestingPlatformBuilderHook.AddExtensions(builder, cliArgs); + Microsoft.Testing.Extensions.Telemetry.TestingPlatformBuilderHook.AddExtensions(builder, cliArgs); + TestingPlatformBuilderHook.AddExtensions(builder, cliArgs); + using ITestApplication app = await builder.BuildAsync(); + await app.RunAsync(); + } + finally + { + _window.Close(); + } +#else + Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.Run(Environment.CommandLine); +#endif + } + + private Window _window; +} diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/LockScreenLogo.scale-200.png b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 0000000000..7440f0d4bf Binary files /dev/null and b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/LockScreenLogo.scale-200.png differ diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/SplashScreen.scale-200.png b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/SplashScreen.scale-200.png new file mode 100644 index 0000000000..32f486a867 Binary files /dev/null and b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/SplashScreen.scale-200.png differ diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/Square150x150Logo.scale-200.png b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 0000000000..53ee3777ea Binary files /dev/null and b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/Square150x150Logo.scale-200.png differ diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/Square44x44Logo.scale-200.png b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 0000000000..f713bba67f Binary files /dev/null and b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/Square44x44Logo.scale-200.png differ diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 0000000000..dc9f5bea0c Binary files /dev/null and b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/StoreLogo.png b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/StoreLogo.png new file mode 100644 index 0000000000..a4586f26bd Binary files /dev/null and b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/StoreLogo.png differ diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/Wide310x150Logo.scale-200.png b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000000..8b4a5d0dd5 Binary files /dev/null and b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Assets/Wide310x150Logo.scale-200.png differ diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/MainWindow.xaml b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/MainWindow.xaml new file mode 100644 index 0000000000..a139c9fb1d --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/MainWindow.xaml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/MainWindow.xaml.cs b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/MainWindow.xaml.cs new file mode 100644 index 0000000000..58e846bcb5 --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/MainWindow.xaml.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.UI.Xaml; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace UnitTest; + +/// +/// An empty window that can be used on its own or navigated to within a Frame. +/// +public sealed partial class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); + } +} diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Package.appxmanifest b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Package.appxmanifest new file mode 100644 index 0000000000..b86d7f7bc0 --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Package.appxmanifest @@ -0,0 +1,48 @@ + + + + + + + + UnitTest + naver + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Properties/PublishProfiles/win10-arm64.pubxml b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Properties/PublishProfiles/win10-arm64.pubxml new file mode 100644 index 0000000000..a7fdd16b67 --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Properties/PublishProfiles/win10-arm64.pubxml @@ -0,0 +1,20 @@ + + + + + FileSystem + ARM64 + win10-arm64 + bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ + true + False + False + True + + + \ No newline at end of file diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Properties/PublishProfiles/win10-x64.pubxml b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Properties/PublishProfiles/win10-x64.pubxml new file mode 100644 index 0000000000..26ea7e55c1 --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Properties/PublishProfiles/win10-x64.pubxml @@ -0,0 +1,20 @@ + + + + + FileSystem + x64 + win10-x64 + bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ + true + False + False + True + + + \ No newline at end of file diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Properties/PublishProfiles/win10-x86.pubxml b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Properties/PublishProfiles/win10-x86.pubxml new file mode 100644 index 0000000000..34d14d4d4a --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Properties/PublishProfiles/win10-x86.pubxml @@ -0,0 +1,20 @@ + + + + + FileSystem + x86 + win10-x86 + bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ + true + False + False + True + + + \ No newline at end of file diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Properties/launchSettings.json b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Properties/launchSettings.json new file mode 100644 index 0000000000..5f98bdda88 --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/Properties/launchSettings.json @@ -0,0 +1,20 @@ +{ + "profiles": { + "UnitTest MSTest Runner (Package)": { + "commandName": "MsixPackage", + "commandLineArgs": "/p:EnableMSTestRunner=true" + }, + "UnitTest MSTest Runner (Unpackaged)": { + "commandName": "Project", + "commandLineArgs": "/p:EnableMSTestRunner=true" + }, + "UnitTest VSTest (Package)": { + "commandName": "MsixPackage", + "commandLineArgs": "/p:EnableMSTestRunner=false" + }, + "UnitTest VSTest (Unpackaged)": { + "commandName": "Project", + "commandLineArgs": "/p:EnableMSTestRunner=false" + } + } +} diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/TestClass.cs b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/TestClass.cs new file mode 100644 index 0000000000..9762a23be6 --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/TestClass.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.UI.Xaml.Controls; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; + +namespace UnitTest; + +[TestClass] +public class TestClass +{ + [UITestMethod] + public void TestMethod1() + { + var grid = new Grid(); + Assert.IsNotNull(grid); + } +} diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/UnitTest.csproj b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/UnitTest.csproj new file mode 100644 index 0000000000..f76ed4a78c --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/UnitTest.csproj @@ -0,0 +1,65 @@ + + + WinExe + net6.0-windows10.0.19041.0 + 10.0.17763.0 + UnitTest + app.manifest + x86;x64;ARM64 + win10-x86;win10-x64;win10-arm64 + win10-$(Platform).pubxml + true + true + + + + + true + false + $(DefineConstants);MSTEST_RUNNER + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + true + + diff --git a/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/app.manifest b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/app.manifest new file mode 100644 index 0000000000..d8d425fd90 --- /dev/null +++ b/samples/public/mstest-runner/MSTestRunnerWinUI/UnitTest/app.manifest @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + true/PM + PerMonitorV2, PerMonitor + + + \ No newline at end of file diff --git a/samples/mstest-runner/NativeAotRunner/ClassLibrary1/Class1.cs b/samples/public/mstest-runner/NativeAotRunner/ClassLibrary1/Class1.cs similarity index 100% rename from samples/mstest-runner/NativeAotRunner/ClassLibrary1/Class1.cs rename to samples/public/mstest-runner/NativeAotRunner/ClassLibrary1/Class1.cs diff --git a/samples/mstest-runner/NativeAotRunner/ClassLibrary1/ClassLibrary1.csproj b/samples/public/mstest-runner/NativeAotRunner/ClassLibrary1/ClassLibrary1.csproj similarity index 100% rename from samples/mstest-runner/NativeAotRunner/ClassLibrary1/ClassLibrary1.csproj rename to samples/public/mstest-runner/NativeAotRunner/ClassLibrary1/ClassLibrary1.csproj diff --git a/samples/mstest-runner/NativeAotRunner/TestProject1.sln b/samples/public/mstest-runner/NativeAotRunner/TestProject1.sln similarity index 100% rename from samples/mstest-runner/NativeAotRunner/TestProject1.sln rename to samples/public/mstest-runner/NativeAotRunner/TestProject1.sln diff --git a/samples/mstest-runner/NativeAotRunner/TestProject1/TestProject1.csproj b/samples/public/mstest-runner/NativeAotRunner/TestProject1/TestProject1.csproj similarity index 63% rename from samples/mstest-runner/NativeAotRunner/TestProject1/TestProject1.csproj rename to samples/public/mstest-runner/NativeAotRunner/TestProject1/TestProject1.csproj index 8445454ea3..89b1a96a9f 100644 --- a/samples/mstest-runner/NativeAotRunner/TestProject1/TestProject1.csproj +++ b/samples/public/mstest-runner/NativeAotRunner/TestProject1/TestProject1.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -10,25 +10,25 @@ - - - + + - - + + - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/samples/mstest-runner/NativeAotRunner/TestProject1/UnitTest1.cs b/samples/public/mstest-runner/NativeAotRunner/TestProject1/UnitTest1.cs similarity index 100% rename from samples/mstest-runner/NativeAotRunner/TestProject1/UnitTest1.cs rename to samples/public/mstest-runner/NativeAotRunner/TestProject1/UnitTest1.cs diff --git a/samples/mstest-runner/RunInDocker/.dockerignore b/samples/public/mstest-runner/RunInDocker/.dockerignore similarity index 100% rename from samples/mstest-runner/RunInDocker/.dockerignore rename to samples/public/mstest-runner/RunInDocker/.dockerignore diff --git a/samples/mstest-runner/RunInDocker/Dockerfile b/samples/public/mstest-runner/RunInDocker/Dockerfile similarity index 100% rename from samples/mstest-runner/RunInDocker/Dockerfile rename to samples/public/mstest-runner/RunInDocker/Dockerfile diff --git a/samples/mstest-runner/RunInDocker/MyServer.Tests/MyServer.Tests.csproj b/samples/public/mstest-runner/RunInDocker/MyServer.Tests/MyServer.Tests.csproj similarity index 86% rename from samples/mstest-runner/RunInDocker/MyServer.Tests/MyServer.Tests.csproj rename to samples/public/mstest-runner/RunInDocker/MyServer.Tests/MyServer.Tests.csproj index 87917d25ff..f60c60fc7b 100644 --- a/samples/mstest-runner/RunInDocker/MyServer.Tests/MyServer.Tests.csproj +++ b/samples/public/mstest-runner/RunInDocker/MyServer.Tests/MyServer.Tests.csproj @@ -3,7 +3,7 @@ true Exe - + net8.0 enable enable @@ -12,7 +12,7 @@ - + diff --git a/samples/mstest-runner/RunInDocker/MyServer.Tests/UnitTest1.cs b/samples/public/mstest-runner/RunInDocker/MyServer.Tests/UnitTest1.cs similarity index 100% rename from samples/mstest-runner/RunInDocker/MyServer.Tests/UnitTest1.cs rename to samples/public/mstest-runner/RunInDocker/MyServer.Tests/UnitTest1.cs diff --git a/samples/mstest-runner/RunInDocker/MyServer/MyServer.csproj b/samples/public/mstest-runner/RunInDocker/MyServer/MyServer.csproj similarity index 100% rename from samples/mstest-runner/RunInDocker/MyServer/MyServer.csproj rename to samples/public/mstest-runner/RunInDocker/MyServer/MyServer.csproj diff --git a/samples/mstest-runner/RunInDocker/MyServer/Program.cs b/samples/public/mstest-runner/RunInDocker/MyServer/Program.cs similarity index 100% rename from samples/mstest-runner/RunInDocker/MyServer/Program.cs rename to samples/public/mstest-runner/RunInDocker/MyServer/Program.cs diff --git a/samples/mstest-runner/RunInDocker/MyServer/Properties/launchSettings.json b/samples/public/mstest-runner/RunInDocker/MyServer/Properties/launchSettings.json similarity index 100% rename from samples/mstest-runner/RunInDocker/MyServer/Properties/launchSettings.json rename to samples/public/mstest-runner/RunInDocker/MyServer/Properties/launchSettings.json diff --git a/samples/mstest-runner/RunInDocker/MyServer/appsettings.Development.json b/samples/public/mstest-runner/RunInDocker/MyServer/appsettings.Development.json similarity index 100% rename from samples/mstest-runner/RunInDocker/MyServer/appsettings.Development.json rename to samples/public/mstest-runner/RunInDocker/MyServer/appsettings.Development.json diff --git a/samples/mstest-runner/RunInDocker/MyServer/appsettings.json b/samples/public/mstest-runner/RunInDocker/MyServer/appsettings.json similarity index 100% rename from samples/mstest-runner/RunInDocker/MyServer/appsettings.json rename to samples/public/mstest-runner/RunInDocker/MyServer/appsettings.json diff --git a/samples/mstest-runner/RunInDocker/README.md b/samples/public/mstest-runner/RunInDocker/README.md similarity index 100% rename from samples/mstest-runner/RunInDocker/README.md rename to samples/public/mstest-runner/RunInDocker/README.md diff --git a/samples/mstest-runner/RunInDocker/RunInDocker.sln b/samples/public/mstest-runner/RunInDocker/RunInDocker.sln similarity index 100% rename from samples/mstest-runner/RunInDocker/RunInDocker.sln rename to samples/public/mstest-runner/RunInDocker/RunInDocker.sln diff --git a/samples/mstest-runner/RunInDocker/global.json b/samples/public/mstest-runner/RunInDocker/global.json similarity index 100% rename from samples/mstest-runner/RunInDocker/global.json rename to samples/public/mstest-runner/RunInDocker/global.json diff --git a/samples/mstest-runner/Simple1/Simple1.csproj b/samples/public/mstest-runner/Simple1/Simple1.csproj similarity index 90% rename from samples/mstest-runner/Simple1/Simple1.csproj rename to samples/public/mstest-runner/Simple1/Simple1.csproj index 2830d71a7c..b3e6042830 100644 --- a/samples/mstest-runner/Simple1/Simple1.csproj +++ b/samples/public/mstest-runner/Simple1/Simple1.csproj @@ -21,7 +21,7 @@ MSTest.TestFramework MSTest.Analyzers --> - + - + diff --git a/samples/mstest-runner/Simple1/TestConfiguration.cs b/samples/public/mstest-runner/Simple1/TestConfiguration.cs similarity index 100% rename from samples/mstest-runner/Simple1/TestConfiguration.cs rename to samples/public/mstest-runner/Simple1/TestConfiguration.cs diff --git a/samples/mstest-runner/Simple1/UnitTest1.cs b/samples/public/mstest-runner/Simple1/UnitTest1.cs similarity index 100% rename from samples/mstest-runner/Simple1/UnitTest1.cs rename to samples/public/mstest-runner/Simple1/UnitTest1.cs diff --git a/samples/mstest-runner/Simple1/global.json b/samples/public/mstest-runner/Simple1/global.json similarity index 100% rename from samples/mstest-runner/Simple1/global.json rename to samples/public/mstest-runner/Simple1/global.json diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/1000C100M.csproj b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/1000C100M.csproj similarity index 75% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/1000C100M.csproj rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/1000C100M.csproj index 0e20449f63..41d0a5b53d 100644 --- a/samples/mstest-runner/runner_vs_vstest/1000C_100M/1000C100M.csproj +++ b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/1000C100M.csproj @@ -12,9 +12,9 @@ - - - + + + diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MSTestConfiguration.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MSTestConfiguration.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MSTestConfiguration.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MSTestConfiguration.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0001.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0001.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0001.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0001.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0002.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0002.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0002.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0002.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0003.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0003.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0003.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0003.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0004.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0004.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0004.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0004.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0005.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0005.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0005.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0005.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0006.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0006.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0006.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0006.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0007.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0007.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0007.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0007.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0008.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0008.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0008.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0008.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0009.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0009.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0009.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0009.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0010.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0010.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0010.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0010.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0011.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0011.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0011.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0011.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0012.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0012.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0012.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0012.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0013.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0013.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0013.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0013.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0014.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0014.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0014.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0014.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0015.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0015.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0015.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0015.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0016.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0016.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0016.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0016.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0017.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0017.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0017.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0017.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0018.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0018.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0018.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0018.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0019.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0019.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0019.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0019.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0020.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0020.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0020.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0020.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0021.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0021.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0021.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0021.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0022.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0022.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0022.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0022.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0023.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0023.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0023.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0023.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0024.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0024.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0024.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0024.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0025.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0025.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0025.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0025.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0026.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0026.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0026.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0026.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0027.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0027.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0027.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0027.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0028.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0028.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0028.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0028.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0029.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0029.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0029.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0029.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0030.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0030.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0030.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0030.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0031.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0031.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0031.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0031.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0032.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0032.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0032.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0032.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0033.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0033.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0033.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0033.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0034.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0034.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0034.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0034.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0035.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0035.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0035.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0035.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0036.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0036.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0036.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0036.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0037.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0037.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0037.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0037.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0038.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0038.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0038.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0038.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0039.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0039.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0039.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0039.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0040.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0040.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0040.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0040.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0041.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0041.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0041.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0041.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0042.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0042.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0042.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0042.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0043.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0043.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0043.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0043.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0044.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0044.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0044.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0044.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0045.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0045.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0045.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0045.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0046.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0046.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0046.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0046.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0047.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0047.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0047.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0047.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0048.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0048.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0048.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0048.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0049.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0049.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0049.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0049.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0050.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0050.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0050.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0050.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0051.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0051.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0051.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0051.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0052.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0052.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0052.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0052.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0053.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0053.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0053.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0053.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0054.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0054.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0054.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0054.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0055.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0055.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0055.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0055.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0056.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0056.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0056.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0056.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0057.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0057.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0057.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0057.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0058.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0058.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0058.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0058.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0059.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0059.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0059.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0059.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0060.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0060.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0060.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0060.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0061.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0061.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0061.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0061.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0062.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0062.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0062.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0062.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0063.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0063.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0063.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0063.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0064.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0064.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0064.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0064.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0065.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0065.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0065.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0065.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0066.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0066.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0066.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0066.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0067.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0067.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0067.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0067.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0068.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0068.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0068.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0068.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0069.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0069.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0069.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0069.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0070.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0070.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0070.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0070.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0071.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0071.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0071.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0071.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0072.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0072.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0072.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0072.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0073.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0073.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0073.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0073.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0074.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0074.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0074.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0074.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0075.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0075.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0075.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0075.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0076.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0076.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0076.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0076.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0077.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0077.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0077.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0077.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0078.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0078.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0078.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0078.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0079.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0079.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0079.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0079.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0080.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0080.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0080.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0080.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0081.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0081.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0081.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0081.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0082.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0082.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0082.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0082.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0083.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0083.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0083.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0083.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0084.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0084.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0084.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0084.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0085.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0085.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0085.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0085.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0086.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0086.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0086.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0086.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0087.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0087.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0087.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0087.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0088.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0088.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0088.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0088.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0089.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0089.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0089.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0089.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0090.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0090.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0090.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0090.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0091.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0091.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0091.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0091.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0092.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0092.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0092.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0092.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0093.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0093.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0093.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0093.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0094.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0094.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0094.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0094.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0095.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0095.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0095.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0095.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0096.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0096.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0096.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0096.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0097.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0097.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0097.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0097.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0098.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0098.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0098.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0098.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0099.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0099.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0099.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0099.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0100.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0100.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0100.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0100.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0101.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0101.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0101.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0101.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0102.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0102.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0102.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0102.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0103.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0103.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0103.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0103.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0104.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0104.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0104.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0104.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0105.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0105.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0105.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0105.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0106.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0106.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0106.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0106.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0107.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0107.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0107.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0107.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0108.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0108.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0108.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0108.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0109.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0109.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0109.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0109.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0110.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0110.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0110.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0110.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0111.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0111.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0111.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0111.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0112.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0112.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0112.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0112.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0113.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0113.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0113.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0113.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0114.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0114.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0114.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0114.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0115.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0115.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0115.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0115.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0116.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0116.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0116.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0116.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0117.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0117.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0117.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0117.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0118.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0118.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0118.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0118.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0119.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0119.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0119.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0119.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0120.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0120.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0120.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0120.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0121.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0121.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0121.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0121.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0122.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0122.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0122.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0122.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0123.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0123.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0123.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0123.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0124.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0124.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0124.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0124.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0125.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0125.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0125.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0125.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0126.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0126.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0126.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0126.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0127.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0127.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0127.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0127.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0128.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0128.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0128.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0128.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0129.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0129.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0129.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0129.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0130.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0130.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0130.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0130.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0131.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0131.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0131.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0131.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0132.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0132.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0132.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0132.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0133.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0133.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0133.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0133.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0134.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0134.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0134.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0134.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0135.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0135.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0135.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0135.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0136.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0136.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0136.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0136.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0137.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0137.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0137.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0137.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0138.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0138.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0138.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0138.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0139.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0139.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0139.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0139.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0140.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0140.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0140.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0140.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0141.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0141.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0141.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0141.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0142.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0142.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0142.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0142.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0143.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0143.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0143.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0143.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0144.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0144.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0144.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0144.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0145.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0145.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0145.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0145.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0146.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0146.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0146.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0146.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0147.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0147.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0147.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0147.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0148.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0148.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0148.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0148.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0149.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0149.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0149.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0149.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0150.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0150.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0150.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0150.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0151.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0151.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0151.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0151.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0152.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0152.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0152.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0152.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0153.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0153.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0153.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0153.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0154.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0154.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0154.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0154.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0155.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0155.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0155.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0155.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0156.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0156.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0156.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0156.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0157.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0157.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0157.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0157.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0158.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0158.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0158.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0158.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0159.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0159.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0159.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0159.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0160.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0160.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0160.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0160.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0161.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0161.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0161.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0161.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0162.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0162.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0162.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0162.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0163.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0163.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0163.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0163.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0164.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0164.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0164.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0164.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0165.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0165.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0165.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0165.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0166.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0166.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0166.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0166.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0167.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0167.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0167.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0167.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0168.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0168.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0168.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0168.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0169.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0169.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0169.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0169.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0170.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0170.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0170.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0170.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0171.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0171.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0171.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0171.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0172.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0172.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0172.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0172.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0173.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0173.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0173.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0173.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0174.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0174.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0174.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0174.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0175.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0175.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0175.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0175.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0176.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0176.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0176.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0176.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0177.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0177.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0177.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0177.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0178.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0178.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0178.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0178.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0179.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0179.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0179.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0179.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0180.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0180.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0180.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0180.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0181.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0181.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0181.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0181.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0182.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0182.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0182.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0182.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0183.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0183.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0183.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0183.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0184.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0184.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0184.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0184.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0185.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0185.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0185.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0185.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0186.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0186.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0186.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0186.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0187.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0187.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0187.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0187.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0188.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0188.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0188.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0188.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0189.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0189.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0189.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0189.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0190.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0190.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0190.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0190.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0191.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0191.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0191.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0191.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0192.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0192.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0192.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0192.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0193.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0193.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0193.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0193.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0194.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0194.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0194.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0194.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0195.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0195.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0195.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0195.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0196.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0196.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0196.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0196.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0197.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0197.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0197.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0197.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0198.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0198.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0198.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0198.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0199.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0199.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0199.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0199.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0200.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0200.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0200.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0200.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0201.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0201.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0201.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0201.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0202.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0202.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0202.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0202.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0203.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0203.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0203.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0203.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0204.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0204.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0204.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0204.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0205.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0205.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0205.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0205.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0206.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0206.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0206.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0206.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0207.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0207.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0207.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0207.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0208.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0208.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0208.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0208.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0209.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0209.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0209.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0209.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0210.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0210.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0210.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0210.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0211.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0211.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0211.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0211.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0212.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0212.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0212.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0212.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0213.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0213.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0213.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0213.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0214.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0214.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0214.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0214.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0215.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0215.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0215.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0215.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0216.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0216.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0216.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0216.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0217.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0217.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0217.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0217.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0218.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0218.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0218.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0218.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0219.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0219.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0219.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0219.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0220.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0220.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0220.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0220.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0221.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0221.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0221.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0221.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0222.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0222.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0222.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0222.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0223.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0223.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0223.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0223.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0224.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0224.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0224.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0224.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0225.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0225.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0225.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0225.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0226.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0226.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0226.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0226.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0227.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0227.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0227.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0227.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0228.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0228.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0228.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0228.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0229.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0229.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0229.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0229.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0230.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0230.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0230.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0230.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0231.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0231.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0231.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0231.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0232.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0232.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0232.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0232.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0233.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0233.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0233.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0233.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0234.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0234.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0234.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0234.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0235.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0235.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0235.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0235.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0236.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0236.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0236.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0236.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0237.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0237.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0237.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0237.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0238.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0238.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0238.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0238.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0239.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0239.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0239.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0239.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0240.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0240.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0240.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0240.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0241.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0241.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0241.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0241.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0242.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0242.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0242.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0242.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0243.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0243.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0243.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0243.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0244.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0244.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0244.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0244.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0245.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0245.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0245.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0245.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0246.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0246.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0246.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0246.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0247.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0247.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0247.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0247.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0248.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0248.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0248.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0248.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0249.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0249.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0249.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0249.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0250.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0250.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0250.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0250.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0251.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0251.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0251.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0251.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0252.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0252.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0252.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0252.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0253.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0253.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0253.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0253.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0254.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0254.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0254.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0254.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0255.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0255.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0255.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0255.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0256.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0256.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0256.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0256.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0257.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0257.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0257.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0257.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0258.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0258.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0258.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0258.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0259.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0259.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0259.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0259.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0260.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0260.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0260.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0260.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0261.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0261.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0261.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0261.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0262.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0262.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0262.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0262.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0263.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0263.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0263.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0263.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0264.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0264.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0264.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0264.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0265.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0265.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0265.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0265.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0266.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0266.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0266.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0266.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0267.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0267.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0267.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0267.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0268.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0268.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0268.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0268.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0269.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0269.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0269.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0269.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0270.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0270.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0270.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0270.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0271.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0271.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0271.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0271.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0272.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0272.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0272.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0272.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0273.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0273.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0273.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0273.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0274.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0274.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0274.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0274.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0275.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0275.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0275.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0275.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0276.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0276.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0276.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0276.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0277.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0277.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0277.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0277.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0278.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0278.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0278.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0278.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0279.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0279.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0279.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0279.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0280.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0280.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0280.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0280.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0281.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0281.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0281.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0281.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0282.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0282.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0282.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0282.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0283.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0283.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0283.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0283.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0284.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0284.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0284.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0284.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0285.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0285.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0285.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0285.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0286.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0286.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0286.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0286.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0287.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0287.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0287.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0287.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0288.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0288.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0288.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0288.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0289.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0289.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0289.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0289.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0290.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0290.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0290.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0290.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0291.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0291.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0291.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0291.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0292.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0292.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0292.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0292.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0293.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0293.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0293.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0293.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0294.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0294.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0294.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0294.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0295.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0295.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0295.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0295.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0296.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0296.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0296.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0296.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0297.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0297.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0297.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0297.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0298.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0298.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0298.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0298.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0299.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0299.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0299.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0299.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0300.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0300.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0300.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0300.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0301.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0301.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0301.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0301.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0302.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0302.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0302.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0302.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0303.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0303.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0303.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0303.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0304.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0304.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0304.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0304.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0305.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0305.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0305.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0305.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0306.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0306.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0306.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0306.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0307.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0307.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0307.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0307.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0308.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0308.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0308.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0308.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0309.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0309.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0309.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0309.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0310.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0310.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0310.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0310.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0311.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0311.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0311.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0311.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0312.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0312.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0312.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0312.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0313.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0313.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0313.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0313.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0314.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0314.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0314.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0314.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0315.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0315.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0315.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0315.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0316.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0316.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0316.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0316.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0317.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0317.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0317.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0317.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0318.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0318.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0318.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0318.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0319.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0319.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0319.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0319.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0320.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0320.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0320.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0320.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0321.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0321.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0321.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0321.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0322.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0322.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0322.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0322.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0323.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0323.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0323.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0323.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0324.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0324.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0324.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0324.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0325.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0325.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0325.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0325.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0326.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0326.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0326.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0326.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0327.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0327.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0327.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0327.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0328.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0328.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0328.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0328.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0329.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0329.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0329.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0329.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0330.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0330.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0330.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0330.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0331.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0331.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0331.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0331.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0332.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0332.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0332.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0332.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0333.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0333.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0333.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0333.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0334.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0334.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0334.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0334.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0335.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0335.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0335.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0335.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0336.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0336.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0336.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0336.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0337.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0337.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0337.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0337.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0338.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0338.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0338.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0338.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0339.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0339.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0339.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0339.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0340.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0340.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0340.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0340.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0341.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0341.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0341.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0341.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0342.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0342.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0342.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0342.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0343.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0343.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0343.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0343.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0344.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0344.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0344.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0344.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0345.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0345.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0345.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0345.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0346.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0346.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0346.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0346.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0347.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0347.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0347.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0347.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0348.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0348.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0348.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0348.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0349.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0349.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0349.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0349.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0350.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0350.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0350.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0350.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0351.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0351.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0351.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0351.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0352.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0352.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0352.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0352.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0353.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0353.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0353.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0353.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0354.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0354.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0354.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0354.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0355.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0355.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0355.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0355.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0356.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0356.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0356.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0356.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0357.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0357.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0357.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0357.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0358.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0358.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0358.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0358.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0359.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0359.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0359.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0359.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0360.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0360.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0360.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0360.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0361.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0361.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0361.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0361.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0362.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0362.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0362.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0362.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0363.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0363.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0363.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0363.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0364.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0364.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0364.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0364.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0365.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0365.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0365.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0365.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0366.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0366.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0366.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0366.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0367.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0367.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0367.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0367.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0368.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0368.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0368.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0368.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0369.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0369.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0369.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0369.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0370.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0370.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0370.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0370.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0371.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0371.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0371.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0371.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0372.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0372.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0372.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0372.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0373.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0373.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0373.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0373.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0374.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0374.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0374.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0374.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0375.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0375.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0375.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0375.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0376.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0376.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0376.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0376.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0377.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0377.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0377.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0377.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0378.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0378.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0378.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0378.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0379.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0379.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0379.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0379.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0380.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0380.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0380.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0380.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0381.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0381.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0381.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0381.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0382.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0382.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0382.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0382.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0383.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0383.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0383.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0383.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0384.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0384.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0384.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0384.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0385.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0385.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0385.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0385.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0386.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0386.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0386.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0386.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0387.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0387.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0387.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0387.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0388.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0388.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0388.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0388.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0389.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0389.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0389.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0389.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0390.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0390.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0390.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0390.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0391.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0391.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0391.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0391.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0392.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0392.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0392.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0392.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0393.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0393.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0393.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0393.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0394.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0394.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0394.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0394.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0395.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0395.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0395.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0395.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0396.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0396.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0396.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0396.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0397.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0397.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0397.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0397.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0398.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0398.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0398.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0398.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0399.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0399.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0399.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0399.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0400.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0400.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0400.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0400.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0401.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0401.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0401.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0401.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0402.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0402.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0402.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0402.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0403.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0403.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0403.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0403.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0404.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0404.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0404.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0404.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0405.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0405.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0405.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0405.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0406.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0406.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0406.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0406.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0407.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0407.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0407.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0407.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0408.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0408.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0408.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0408.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0409.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0409.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0409.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0409.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0410.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0410.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0410.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0410.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0411.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0411.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0411.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0411.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0412.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0412.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0412.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0412.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0413.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0413.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0413.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0413.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0414.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0414.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0414.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0414.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0415.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0415.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0415.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0415.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0416.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0416.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0416.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0416.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0417.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0417.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0417.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0417.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0418.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0418.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0418.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0418.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0419.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0419.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0419.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0419.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0420.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0420.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0420.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0420.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0421.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0421.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0421.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0421.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0422.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0422.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0422.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0422.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0423.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0423.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0423.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0423.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0424.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0424.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0424.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0424.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0425.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0425.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0425.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0425.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0426.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0426.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0426.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0426.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0427.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0427.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0427.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0427.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0428.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0428.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0428.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0428.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0429.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0429.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0429.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0429.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0430.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0430.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0430.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0430.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0431.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0431.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0431.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0431.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0432.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0432.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0432.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0432.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0433.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0433.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0433.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0433.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0434.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0434.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0434.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0434.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0435.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0435.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0435.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0435.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0436.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0436.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0436.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0436.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0437.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0437.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0437.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0437.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0438.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0438.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0438.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0438.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0439.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0439.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0439.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0439.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0440.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0440.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0440.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0440.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0441.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0441.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0441.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0441.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0442.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0442.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0442.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0442.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0443.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0443.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0443.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0443.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0444.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0444.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0444.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0444.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0445.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0445.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0445.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0445.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0446.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0446.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0446.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0446.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0447.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0447.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0447.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0447.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0448.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0448.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0448.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0448.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0449.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0449.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0449.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0449.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0450.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0450.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0450.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0450.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0451.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0451.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0451.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0451.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0452.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0452.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0452.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0452.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0453.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0453.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0453.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0453.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0454.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0454.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0454.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0454.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0455.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0455.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0455.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0455.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0456.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0456.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0456.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0456.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0457.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0457.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0457.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0457.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0458.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0458.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0458.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0458.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0459.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0459.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0459.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0459.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0460.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0460.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0460.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0460.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0461.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0461.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0461.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0461.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0462.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0462.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0462.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0462.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0463.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0463.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0463.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0463.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0464.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0464.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0464.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0464.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0465.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0465.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0465.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0465.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0466.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0466.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0466.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0466.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0467.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0467.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0467.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0467.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0468.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0468.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0468.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0468.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0469.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0469.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0469.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0469.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0470.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0470.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0470.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0470.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0471.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0471.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0471.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0471.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0472.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0472.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0472.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0472.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0473.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0473.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0473.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0473.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0474.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0474.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0474.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0474.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0475.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0475.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0475.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0475.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0476.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0476.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0476.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0476.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0477.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0477.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0477.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0477.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0478.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0478.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0478.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0478.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0479.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0479.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0479.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0479.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0480.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0480.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0480.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0480.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0481.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0481.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0481.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0481.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0482.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0482.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0482.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0482.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0483.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0483.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0483.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0483.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0484.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0484.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0484.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0484.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0485.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0485.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0485.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0485.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0486.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0486.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0486.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0486.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0487.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0487.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0487.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0487.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0488.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0488.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0488.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0488.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0489.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0489.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0489.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0489.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0490.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0490.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0490.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0490.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0491.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0491.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0491.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0491.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0492.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0492.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0492.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0492.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0493.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0493.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0493.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0493.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0494.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0494.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0494.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0494.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0495.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0495.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0495.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0495.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0496.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0496.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0496.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0496.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0497.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0497.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0497.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0497.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0498.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0498.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0498.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0498.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0499.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0499.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0499.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0499.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0500.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0500.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0500.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0500.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0501.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0501.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0501.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0501.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0502.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0502.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0502.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0502.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0503.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0503.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0503.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0503.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0504.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0504.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0504.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0504.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0505.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0505.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0505.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0505.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0506.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0506.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0506.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0506.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0507.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0507.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0507.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0507.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0508.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0508.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0508.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0508.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0509.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0509.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0509.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0509.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0510.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0510.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0510.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0510.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0511.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0511.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0511.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0511.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0512.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0512.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0512.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0512.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0513.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0513.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0513.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0513.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0514.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0514.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0514.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0514.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0515.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0515.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0515.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0515.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0516.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0516.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0516.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0516.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0517.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0517.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0517.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0517.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0518.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0518.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0518.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0518.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0519.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0519.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0519.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0519.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0520.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0520.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0520.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0520.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0521.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0521.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0521.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0521.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0522.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0522.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0522.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0522.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0523.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0523.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0523.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0523.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0524.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0524.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0524.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0524.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0525.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0525.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0525.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0525.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0526.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0526.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0526.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0526.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0527.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0527.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0527.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0527.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0528.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0528.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0528.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0528.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0529.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0529.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0529.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0529.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0530.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0530.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0530.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0530.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0531.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0531.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0531.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0531.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0532.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0532.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0532.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0532.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0533.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0533.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0533.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0533.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0534.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0534.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0534.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0534.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0535.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0535.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0535.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0535.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0536.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0536.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0536.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0536.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0537.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0537.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0537.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0537.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0538.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0538.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0538.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0538.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0539.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0539.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0539.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0539.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0540.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0540.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0540.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0540.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0541.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0541.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0541.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0541.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0542.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0542.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0542.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0542.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0543.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0543.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0543.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0543.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0544.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0544.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0544.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0544.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0545.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0545.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0545.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0545.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0546.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0546.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0546.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0546.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0547.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0547.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0547.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0547.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0548.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0548.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0548.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0548.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0549.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0549.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0549.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0549.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0550.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0550.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0550.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0550.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0551.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0551.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0551.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0551.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0552.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0552.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0552.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0552.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0553.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0553.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0553.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0553.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0554.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0554.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0554.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0554.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0555.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0555.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0555.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0555.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0556.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0556.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0556.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0556.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0557.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0557.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0557.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0557.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0558.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0558.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0558.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0558.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0559.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0559.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0559.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0559.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0560.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0560.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0560.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0560.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0561.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0561.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0561.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0561.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0562.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0562.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0562.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0562.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0563.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0563.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0563.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0563.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0564.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0564.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0564.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0564.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0565.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0565.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0565.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0565.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0566.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0566.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0566.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0566.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0567.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0567.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0567.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0567.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0568.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0568.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0568.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0568.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0569.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0569.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0569.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0569.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0570.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0570.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0570.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0570.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0571.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0571.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0571.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0571.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0572.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0572.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0572.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0572.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0573.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0573.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0573.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0573.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0574.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0574.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0574.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0574.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0575.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0575.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0575.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0575.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0576.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0576.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0576.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0576.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0577.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0577.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0577.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0577.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0578.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0578.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0578.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0578.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0579.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0579.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0579.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0579.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0580.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0580.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0580.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0580.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0581.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0581.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0581.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0581.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0582.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0582.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0582.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0582.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0583.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0583.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0583.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0583.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0584.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0584.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0584.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0584.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0585.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0585.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0585.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0585.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0586.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0586.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0586.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0586.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0587.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0587.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0587.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0587.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0588.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0588.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0588.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0588.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0589.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0589.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0589.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0589.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0590.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0590.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0590.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0590.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0591.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0591.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0591.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0591.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0592.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0592.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0592.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0592.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0593.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0593.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0593.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0593.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0594.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0594.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0594.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0594.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0595.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0595.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0595.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0595.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0596.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0596.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0596.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0596.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0597.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0597.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0597.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0597.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0598.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0598.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0598.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0598.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0599.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0599.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0599.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0599.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0600.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0600.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0600.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0600.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0601.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0601.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0601.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0601.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0602.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0602.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0602.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0602.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0603.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0603.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0603.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0603.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0604.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0604.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0604.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0604.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0605.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0605.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0605.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0605.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0606.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0606.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0606.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0606.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0607.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0607.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0607.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0607.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0608.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0608.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0608.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0608.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0609.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0609.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0609.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0609.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0610.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0610.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0610.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0610.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0611.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0611.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0611.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0611.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0612.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0612.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0612.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0612.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0613.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0613.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0613.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0613.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0614.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0614.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0614.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0614.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0615.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0615.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0615.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0615.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0616.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0616.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0616.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0616.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0617.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0617.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0617.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0617.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0618.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0618.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0618.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0618.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0619.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0619.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0619.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0619.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0620.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0620.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0620.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0620.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0621.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0621.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0621.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0621.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0622.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0622.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0622.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0622.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0623.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0623.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0623.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0623.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0624.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0624.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0624.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0624.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0625.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0625.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0625.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0625.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0626.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0626.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0626.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0626.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0627.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0627.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0627.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0627.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0628.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0628.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0628.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0628.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0629.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0629.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0629.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0629.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0630.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0630.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0630.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0630.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0631.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0631.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0631.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0631.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0632.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0632.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0632.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0632.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0633.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0633.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0633.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0633.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0634.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0634.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0634.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0634.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0635.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0635.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0635.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0635.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0636.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0636.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0636.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0636.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0637.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0637.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0637.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0637.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0638.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0638.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0638.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0638.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0639.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0639.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0639.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0639.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0640.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0640.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0640.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0640.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0641.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0641.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0641.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0641.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0642.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0642.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0642.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0642.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0643.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0643.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0643.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0643.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0644.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0644.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0644.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0644.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0645.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0645.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0645.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0645.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0646.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0646.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0646.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0646.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0647.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0647.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0647.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0647.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0648.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0648.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0648.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0648.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0649.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0649.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0649.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0649.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0650.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0650.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0650.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0650.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0651.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0651.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0651.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0651.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0652.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0652.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0652.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0652.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0653.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0653.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0653.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0653.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0654.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0654.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0654.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0654.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0655.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0655.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0655.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0655.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0656.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0656.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0656.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0656.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0657.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0657.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0657.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0657.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0658.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0658.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0658.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0658.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0659.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0659.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0659.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0659.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0660.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0660.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0660.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0660.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0661.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0661.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0661.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0661.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0662.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0662.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0662.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0662.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0663.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0663.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0663.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0663.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0664.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0664.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0664.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0664.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0665.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0665.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0665.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0665.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0666.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0666.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0666.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0666.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0667.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0667.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0667.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0667.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0668.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0668.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0668.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0668.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0669.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0669.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0669.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0669.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0670.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0670.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0670.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0670.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0671.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0671.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0671.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0671.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0672.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0672.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0672.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0672.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0673.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0673.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0673.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0673.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0674.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0674.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0674.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0674.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0675.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0675.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0675.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0675.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0676.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0676.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0676.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0676.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0677.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0677.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0677.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0677.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0678.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0678.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0678.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0678.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0679.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0679.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0679.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0679.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0680.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0680.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0680.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0680.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0681.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0681.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0681.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0681.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0682.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0682.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0682.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0682.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0683.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0683.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0683.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0683.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0684.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0684.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0684.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0684.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0685.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0685.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0685.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0685.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0686.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0686.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0686.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0686.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0687.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0687.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0687.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0687.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0688.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0688.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0688.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0688.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0689.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0689.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0689.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0689.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0690.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0690.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0690.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0690.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0691.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0691.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0691.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0691.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0692.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0692.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0692.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0692.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0693.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0693.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0693.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0693.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0694.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0694.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0694.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0694.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0695.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0695.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0695.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0695.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0696.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0696.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0696.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0696.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0697.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0697.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0697.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0697.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0698.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0698.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0698.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0698.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0699.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0699.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0699.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0699.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0700.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0700.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0700.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0700.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0701.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0701.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0701.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0701.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0702.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0702.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0702.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0702.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0703.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0703.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0703.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0703.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0704.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0704.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0704.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0704.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0705.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0705.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0705.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0705.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0706.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0706.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0706.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0706.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0707.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0707.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0707.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0707.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0708.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0708.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0708.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0708.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0709.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0709.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0709.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0709.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0710.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0710.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0710.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0710.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0711.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0711.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0711.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0711.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0712.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0712.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0712.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0712.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0713.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0713.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0713.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0713.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0714.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0714.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0714.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0714.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0715.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0715.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0715.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0715.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0716.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0716.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0716.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0716.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0717.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0717.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0717.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0717.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0718.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0718.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0718.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0718.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0719.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0719.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0719.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0719.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0720.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0720.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0720.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0720.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0721.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0721.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0721.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0721.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0722.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0722.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0722.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0722.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0723.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0723.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0723.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0723.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0724.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0724.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0724.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0724.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0725.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0725.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0725.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0725.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0726.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0726.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0726.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0726.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0727.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0727.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0727.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0727.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0728.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0728.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0728.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0728.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0729.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0729.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0729.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0729.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0730.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0730.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0730.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0730.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0731.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0731.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0731.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0731.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0732.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0732.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0732.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0732.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0733.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0733.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0733.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0733.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0734.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0734.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0734.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0734.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0735.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0735.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0735.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0735.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0736.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0736.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0736.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0736.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0737.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0737.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0737.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0737.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0738.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0738.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0738.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0738.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0739.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0739.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0739.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0739.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0740.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0740.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0740.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0740.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0741.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0741.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0741.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0741.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0742.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0742.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0742.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0742.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0743.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0743.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0743.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0743.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0744.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0744.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0744.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0744.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0745.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0745.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0745.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0745.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0746.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0746.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0746.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0746.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0747.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0747.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0747.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0747.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0748.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0748.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0748.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0748.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0749.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0749.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0749.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0749.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0750.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0750.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0750.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0750.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0751.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0751.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0751.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0751.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0752.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0752.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0752.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0752.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0753.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0753.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0753.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0753.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0754.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0754.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0754.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0754.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0755.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0755.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0755.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0755.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0756.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0756.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0756.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0756.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0757.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0757.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0757.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0757.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0758.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0758.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0758.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0758.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0759.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0759.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0759.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0759.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0760.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0760.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0760.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0760.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0761.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0761.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0761.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0761.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0762.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0762.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0762.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0762.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0763.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0763.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0763.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0763.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0764.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0764.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0764.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0764.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0765.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0765.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0765.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0765.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0766.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0766.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0766.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0766.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0767.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0767.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0767.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0767.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0768.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0768.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0768.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0768.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0769.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0769.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0769.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0769.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0770.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0770.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0770.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0770.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0771.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0771.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0771.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0771.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0772.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0772.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0772.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0772.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0773.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0773.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0773.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0773.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0774.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0774.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0774.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0774.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0775.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0775.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0775.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0775.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0776.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0776.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0776.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0776.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0777.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0777.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0777.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0777.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0778.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0778.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0778.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0778.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0779.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0779.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0779.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0779.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0780.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0780.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0780.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0780.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0781.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0781.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0781.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0781.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0782.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0782.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0782.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0782.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0783.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0783.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0783.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0783.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0784.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0784.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0784.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0784.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0785.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0785.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0785.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0785.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0786.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0786.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0786.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0786.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0787.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0787.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0787.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0787.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0788.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0788.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0788.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0788.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0789.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0789.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0789.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0789.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0790.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0790.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0790.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0790.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0791.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0791.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0791.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0791.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0792.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0792.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0792.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0792.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0793.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0793.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0793.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0793.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0794.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0794.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0794.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0794.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0795.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0795.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0795.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0795.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0796.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0796.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0796.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0796.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0797.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0797.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0797.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0797.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0798.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0798.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0798.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0798.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0799.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0799.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0799.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0799.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0800.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0800.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0800.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0800.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0801.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0801.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0801.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0801.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0802.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0802.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0802.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0802.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0803.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0803.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0803.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0803.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0804.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0804.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0804.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0804.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0805.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0805.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0805.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0805.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0806.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0806.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0806.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0806.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0807.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0807.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0807.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0807.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0808.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0808.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0808.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0808.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0809.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0809.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0809.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0809.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0810.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0810.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0810.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0810.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0811.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0811.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0811.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0811.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0812.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0812.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0812.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0812.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0813.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0813.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0813.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0813.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0814.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0814.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0814.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0814.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0815.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0815.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0815.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0815.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0816.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0816.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0816.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0816.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0817.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0817.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0817.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0817.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0818.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0818.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0818.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0818.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0819.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0819.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0819.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0819.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0820.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0820.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0820.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0820.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0821.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0821.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0821.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0821.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0822.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0822.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0822.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0822.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0823.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0823.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0823.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0823.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0824.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0824.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0824.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0824.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0825.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0825.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0825.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0825.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0826.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0826.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0826.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0826.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0827.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0827.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0827.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0827.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0828.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0828.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0828.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0828.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0829.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0829.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0829.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0829.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0830.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0830.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0830.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0830.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0831.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0831.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0831.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0831.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0832.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0832.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0832.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0832.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0833.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0833.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0833.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0833.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0834.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0834.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0834.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0834.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0835.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0835.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0835.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0835.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0836.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0836.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0836.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0836.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0837.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0837.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0837.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0837.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0838.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0838.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0838.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0838.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0839.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0839.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0839.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0839.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0840.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0840.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0840.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0840.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0841.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0841.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0841.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0841.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0842.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0842.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0842.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0842.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0843.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0843.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0843.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0843.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0844.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0844.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0844.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0844.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0845.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0845.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0845.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0845.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0846.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0846.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0846.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0846.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0847.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0847.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0847.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0847.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0848.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0848.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0848.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0848.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0849.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0849.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0849.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0849.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0850.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0850.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0850.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0850.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0851.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0851.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0851.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0851.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0852.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0852.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0852.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0852.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0853.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0853.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0853.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0853.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0854.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0854.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0854.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0854.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0855.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0855.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0855.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0855.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0856.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0856.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0856.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0856.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0857.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0857.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0857.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0857.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0858.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0858.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0858.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0858.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0859.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0859.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0859.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0859.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0860.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0860.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0860.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0860.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0861.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0861.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0861.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0861.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0862.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0862.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0862.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0862.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0863.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0863.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0863.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0863.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0864.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0864.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0864.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0864.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0865.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0865.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0865.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0865.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0866.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0866.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0866.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0866.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0867.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0867.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0867.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0867.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0868.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0868.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0868.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0868.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0869.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0869.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0869.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0869.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0870.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0870.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0870.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0870.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0871.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0871.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0871.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0871.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0872.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0872.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0872.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0872.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0873.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0873.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0873.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0873.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0874.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0874.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0874.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0874.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0875.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0875.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0875.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0875.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0876.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0876.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0876.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0876.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0877.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0877.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0877.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0877.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0878.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0878.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0878.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0878.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0879.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0879.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0879.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0879.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0880.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0880.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0880.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0880.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0881.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0881.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0881.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0881.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0882.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0882.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0882.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0882.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0883.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0883.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0883.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0883.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0884.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0884.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0884.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0884.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0885.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0885.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0885.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0885.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0886.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0886.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0886.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0886.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0887.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0887.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0887.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0887.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0888.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0888.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0888.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0888.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0889.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0889.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0889.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0889.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0890.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0890.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0890.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0890.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0891.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0891.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0891.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0891.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0892.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0892.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0892.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0892.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0893.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0893.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0893.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0893.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0894.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0894.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0894.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0894.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0895.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0895.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0895.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0895.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0896.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0896.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0896.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0896.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0897.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0897.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0897.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0897.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0898.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0898.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0898.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0898.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0899.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0899.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0899.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0899.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0900.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0900.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0900.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0900.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0901.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0901.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0901.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0901.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0902.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0902.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0902.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0902.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0903.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0903.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0903.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0903.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0904.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0904.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0904.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0904.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0905.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0905.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0905.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0905.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0906.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0906.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0906.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0906.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0907.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0907.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0907.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0907.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0908.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0908.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0908.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0908.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0909.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0909.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0909.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0909.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0910.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0910.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0910.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0910.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0911.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0911.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0911.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0911.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0912.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0912.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0912.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0912.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0913.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0913.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0913.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0913.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0914.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0914.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0914.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0914.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0915.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0915.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0915.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0915.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0916.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0916.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0916.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0916.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0917.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0917.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0917.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0917.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0918.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0918.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0918.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0918.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0919.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0919.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0919.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0919.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0920.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0920.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0920.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0920.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0921.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0921.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0921.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0921.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0922.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0922.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0922.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0922.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0923.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0923.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0923.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0923.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0924.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0924.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0924.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0924.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0925.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0925.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0925.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0925.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0926.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0926.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0926.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0926.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0927.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0927.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0927.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0927.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0928.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0928.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0928.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0928.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0929.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0929.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0929.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0929.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0930.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0930.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0930.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0930.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0931.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0931.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0931.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0931.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0932.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0932.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0932.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0932.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0933.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0933.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0933.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0933.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0934.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0934.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0934.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0934.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0935.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0935.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0935.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0935.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0936.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0936.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0936.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0936.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0937.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0937.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0937.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0937.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0938.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0938.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0938.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0938.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0939.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0939.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0939.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0939.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0940.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0940.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0940.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0940.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0941.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0941.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0941.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0941.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0942.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0942.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0942.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0942.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0943.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0943.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0943.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0943.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0944.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0944.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0944.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0944.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0945.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0945.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0945.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0945.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0946.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0946.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0946.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0946.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0947.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0947.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0947.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0947.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0948.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0948.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0948.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0948.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0949.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0949.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0949.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0949.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0950.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0950.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0950.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0950.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0951.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0951.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0951.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0951.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0952.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0952.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0952.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0952.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0953.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0953.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0953.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0953.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0954.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0954.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0954.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0954.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0955.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0955.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0955.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0955.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0956.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0956.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0956.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0956.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0957.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0957.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0957.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0957.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0958.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0958.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0958.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0958.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0959.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0959.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0959.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0959.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0960.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0960.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0960.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0960.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0961.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0961.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0961.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0961.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0962.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0962.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0962.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0962.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0963.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0963.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0963.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0963.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0964.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0964.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0964.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0964.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0965.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0965.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0965.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0965.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0966.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0966.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0966.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0966.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0967.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0967.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0967.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0967.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0968.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0968.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0968.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0968.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0969.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0969.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0969.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0969.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0970.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0970.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0970.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0970.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0971.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0971.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0971.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0971.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0972.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0972.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0972.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0972.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0973.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0973.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0973.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0973.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0974.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0974.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0974.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0974.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0975.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0975.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0975.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0975.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0976.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0976.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0976.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0976.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0977.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0977.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0977.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0977.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0978.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0978.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0978.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0978.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0979.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0979.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0979.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0979.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0980.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0980.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0980.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0980.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0981.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0981.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0981.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0981.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0982.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0982.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0982.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0982.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0983.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0983.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0983.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0983.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0984.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0984.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0984.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0984.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0985.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0985.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0985.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0985.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0986.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0986.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0986.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0986.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0987.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0987.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0987.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0987.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0988.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0988.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0988.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0988.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0989.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0989.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0989.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0989.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0990.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0990.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0990.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0990.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0991.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0991.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0991.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0991.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0992.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0992.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0992.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0992.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0993.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0993.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0993.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0993.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0994.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0994.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0994.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0994.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0995.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0995.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0995.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0995.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0996.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0996.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0996.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0996.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0997.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0997.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0997.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0997.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0998.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0998.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0998.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0998.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0999.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0999.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0999.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass0999.cs diff --git a/samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass1000.cs b/samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass1000.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass1000.cs rename to samples/public/mstest-runner/runner_vs_vstest/1000C_100M/MyTestClass1000.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/100C100M.csproj b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/100C100M.csproj similarity index 75% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/100C100M.csproj rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/100C100M.csproj index 0e20449f63..41d0a5b53d 100644 --- a/samples/mstest-runner/runner_vs_vstest/100C_100M/100C100M.csproj +++ b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/100C100M.csproj @@ -12,9 +12,9 @@ - - - + + + diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MSTestConfiguration.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MSTestConfiguration.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MSTestConfiguration.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MSTestConfiguration.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass001.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass001.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass001.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass001.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass002.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass002.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass002.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass002.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass003.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass003.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass003.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass003.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass004.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass004.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass004.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass004.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass005.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass005.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass005.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass005.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass006.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass006.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass006.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass006.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass007.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass007.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass007.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass007.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass008.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass008.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass008.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass008.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass009.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass009.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass009.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass009.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass010.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass010.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass010.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass010.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass011.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass011.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass011.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass011.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass012.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass012.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass012.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass012.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass013.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass013.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass013.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass013.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass014.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass014.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass014.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass014.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass015.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass015.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass015.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass015.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass016.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass016.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass016.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass016.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass017.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass017.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass017.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass017.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass018.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass018.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass018.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass018.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass019.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass019.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass019.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass019.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass020.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass020.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass020.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass020.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass021.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass021.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass021.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass021.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass022.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass022.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass022.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass022.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass023.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass023.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass023.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass023.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass024.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass024.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass024.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass024.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass025.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass025.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass025.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass025.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass026.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass026.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass026.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass026.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass027.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass027.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass027.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass027.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass028.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass028.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass028.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass028.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass029.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass029.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass029.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass029.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass030.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass030.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass030.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass030.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass031.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass031.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass031.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass031.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass032.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass032.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass032.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass032.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass033.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass033.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass033.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass033.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass034.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass034.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass034.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass034.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass035.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass035.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass035.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass035.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass036.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass036.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass036.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass036.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass037.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass037.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass037.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass037.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass038.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass038.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass038.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass038.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass039.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass039.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass039.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass039.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass040.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass040.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass040.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass040.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass041.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass041.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass041.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass041.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass042.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass042.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass042.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass042.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass043.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass043.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass043.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass043.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass044.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass044.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass044.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass044.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass045.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass045.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass045.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass045.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass046.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass046.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass046.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass046.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass047.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass047.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass047.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass047.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass048.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass048.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass048.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass048.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass049.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass049.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass049.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass049.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass050.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass050.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass050.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass050.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass051.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass051.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass051.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass051.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass052.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass052.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass052.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass052.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass053.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass053.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass053.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass053.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass054.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass054.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass054.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass054.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass055.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass055.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass055.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass055.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass056.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass056.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass056.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass056.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass057.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass057.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass057.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass057.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass058.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass058.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass058.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass058.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass059.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass059.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass059.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass059.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass060.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass060.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass060.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass060.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass061.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass061.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass061.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass061.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass062.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass062.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass062.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass062.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass063.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass063.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass063.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass063.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass064.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass064.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass064.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass064.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass065.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass065.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass065.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass065.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass066.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass066.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass066.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass066.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass067.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass067.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass067.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass067.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass068.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass068.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass068.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass068.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass069.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass069.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass069.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass069.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass070.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass070.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass070.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass070.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass071.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass071.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass071.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass071.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass072.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass072.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass072.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass072.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass073.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass073.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass073.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass073.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass074.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass074.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass074.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass074.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass075.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass075.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass075.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass075.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass076.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass076.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass076.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass076.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass077.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass077.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass077.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass077.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass078.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass078.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass078.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass078.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass079.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass079.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass079.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass079.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass080.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass080.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass080.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass080.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass081.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass081.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass081.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass081.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass082.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass082.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass082.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass082.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass083.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass083.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass083.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass083.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass084.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass084.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass084.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass084.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass085.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass085.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass085.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass085.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass086.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass086.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass086.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass086.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass087.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass087.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass087.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass087.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass088.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass088.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass088.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass088.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass089.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass089.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass089.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass089.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass090.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass090.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass090.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass090.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass091.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass091.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass091.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass091.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass092.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass092.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass092.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass092.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass093.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass093.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass093.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass093.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass094.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass094.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass094.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass094.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass095.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass095.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass095.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass095.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass096.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass096.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass096.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass096.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass097.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass097.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass097.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass097.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass098.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass098.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass098.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass098.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass099.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass099.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass099.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass099.cs diff --git a/samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass100.cs b/samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass100.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass100.cs rename to samples/public/mstest-runner/runner_vs_vstest/100C_100M/MyTestClass100.cs diff --git a/samples/mstest-runner/runner_vs_vstest/10C_100M/10C100M.csproj b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/10C100M.csproj similarity index 75% rename from samples/mstest-runner/runner_vs_vstest/10C_100M/10C100M.csproj rename to samples/public/mstest-runner/runner_vs_vstest/10C_100M/10C100M.csproj index 0e20449f63..41d0a5b53d 100644 --- a/samples/mstest-runner/runner_vs_vstest/10C_100M/10C100M.csproj +++ b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/10C100M.csproj @@ -12,9 +12,9 @@ - - - + + + diff --git a/samples/mstest-runner/runner_vs_vstest/10C_100M/MSTestConfiguration.cs b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/MSTestConfiguration.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/10C_100M/MSTestConfiguration.cs rename to samples/public/mstest-runner/runner_vs_vstest/10C_100M/MSTestConfiguration.cs diff --git a/samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass01.cs b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass01.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass01.cs rename to samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass01.cs diff --git a/samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass02.cs b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass02.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass02.cs rename to samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass02.cs diff --git a/samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass03.cs b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass03.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass03.cs rename to samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass03.cs diff --git a/samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass04.cs b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass04.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass04.cs rename to samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass04.cs diff --git a/samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass05.cs b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass05.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass05.cs rename to samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass05.cs diff --git a/samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass06.cs b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass06.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass06.cs rename to samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass06.cs diff --git a/samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass07.cs b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass07.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass07.cs rename to samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass07.cs diff --git a/samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass08.cs b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass08.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass08.cs rename to samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass08.cs diff --git a/samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass09.cs b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass09.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass09.cs rename to samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass09.cs diff --git a/samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass10.cs b/samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass10.cs similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass10.cs rename to samples/public/mstest-runner/runner_vs_vstest/10C_100M/MyTestClass10.cs diff --git a/samples/mstest-runner/runner_vs_vstest/README.md b/samples/public/mstest-runner/runner_vs_vstest/README.md similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/README.md rename to samples/public/mstest-runner/runner_vs_vstest/README.md diff --git a/samples/mstest-runner/runner_vs_vstest/measure.ps1 b/samples/public/mstest-runner/runner_vs_vstest/measure.ps1 similarity index 100% rename from samples/mstest-runner/runner_vs_vstest/measure.ps1 rename to samples/public/mstest-runner/runner_vs_vstest/measure.ps1 diff --git a/src/Adapter/MSTest.TestAdapter/BannedSymbols.txt b/src/Adapter/MSTest.TestAdapter/BannedSymbols.txt new file mode 100644 index 0000000000..91178cbc64 --- /dev/null +++ b/src/Adapter/MSTest.TestAdapter/BannedSymbols.txt @@ -0,0 +1 @@ +M:Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers.ReflectHelper.#ctor; This is allowed only for tests. diff --git a/src/Adapter/MSTest.TestAdapter/Constants.cs b/src/Adapter/MSTest.TestAdapter/Constants.cs index f8f0c0bb54..057ebb6bfa 100644 --- a/src/Adapter/MSTest.TestAdapter/Constants.cs +++ b/src/Adapter/MSTest.TestAdapter/Constants.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.VisualStudio.TestPlatform.ObjectModel; @@ -10,6 +10,36 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// internal static class Constants { + /// + /// The 3rd level entry (class) name in the hierarchy array. + /// + internal const string AssemblyFixturesHierarchyClassName = "[Assembly]"; + + /// + /// Discover fixtures or not. + /// + internal const string FixturesTestTrait = "FixturesTrait"; + + /// + /// Assembly initialize. + /// + internal const string AssemblyInitializeFixtureTrait = "AssemblyInitialize"; + + /// + /// Assembly cleanup. + /// + internal const string AssemblyCleanupFixtureTrait = "AssemblyCleanup"; + + /// + /// Class initialize. + /// + internal const string ClassInitializeFixtureTrait = "ClassInitialize"; + + /// + /// Class cleanup. + /// + internal const string ClassCleanupFixtureTrait = "ClassCleanup"; + /// /// Uri of the MSTest executor. /// diff --git a/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumerator.cs b/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumerator.cs index bf0ec5d305..e2a2668ffd 100644 --- a/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumerator.cs +++ b/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumerator.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; using System.Runtime.Serialization; @@ -10,6 +11,7 @@ using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; using FrameworkITestDataSource = Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSource; @@ -43,12 +45,10 @@ public AssemblyEnumerator() /// /// The settings for the session. /// Use this constructor when creating this object in a new app domain so the settings for this app domain are set. - public AssemblyEnumerator(MSTestSettings settings) - { + public AssemblyEnumerator(MSTestSettings settings) => // Populate the settings into the domain(Desktop workflow) performing discovery. // This would just be resetting the settings to itself in non desktop workflows. MSTestSettings.PopulateSettings(settings); - } /// /// Gets or sets the run settings to use for current discovery session. @@ -76,9 +76,10 @@ public AssemblyEnumerator(MSTestSettings settings) internal ICollection EnumerateAssembly(string assemblyFileName, out ICollection warnings) { DebugEx.Assert(!StringEx.IsNullOrWhiteSpace(assemblyFileName), "Invalid assembly file name."); - var warningMessages = new List(); var tests = new List(); + // Contains list of assembly/class names for which we have already added fixture tests. + var fixturesTests = new HashSet(); Assembly assembly = PlatformServiceProvider.Instance.FileOperations.LoadAssembly(assemblyFileName, isReflectionOnly: false); @@ -87,6 +88,10 @@ internal ICollection EnumerateAssembly(string assemblyFileName, TestIdGenerationStrategy testIdGenerationStrategy = assembly.GetCustomAttribute()?.Strategy ?? TestIdGenerationStrategy.FullyQualified; + // Set the test ID generation strategy for the data row attribute so we can improve display name without causing + // a breaking change. + DataRowAttribute.TestIdGenerationStrategy = testIdGenerationStrategy; + TestDataSourceDiscoveryOption testDataSourceDiscovery = assembly.GetCustomAttribute()?.DiscoveryOption #pragma warning disable CS0618 // Type or member is obsolete @@ -104,7 +109,7 @@ internal ICollection EnumerateAssembly(string assemblyFileName, } List testsInType = DiscoverTestsInType(assemblyFileName, RunSettingsXml, type, warningMessages, discoverInternals, - testDataSourceDiscovery, testIdGenerationStrategy); + testDataSourceDiscovery, testIdGenerationStrategy, fixturesTests); tests.AddRange(testsInType); } @@ -119,12 +124,11 @@ internal ICollection EnumerateAssembly(string assemblyFileName, /// The file name of the assembly. /// Contains warnings if any, that need to be passed back to the caller. /// Gets the types defined in the provided assembly. - internal static IReadOnlyList GetTypes(Assembly assembly, string assemblyFileName, ICollection? warningMessages) + internal static Type[] GetTypes(Assembly assembly, string assemblyFileName, ICollection? warningMessages) { - var types = new List(); try { - types.AddRange(assembly.DefinedTypes.Select(typeInfo => typeInfo.AsType())); + return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { @@ -146,8 +150,6 @@ internal static IReadOnlyList GetTypes(Assembly assembly, string assemblyF return ex.Types!; } - - return types; } /// @@ -191,19 +193,26 @@ internal static string GetLoadExceptionDetails(ReflectionTypeLoadException ex) /// The reflected assembly name. /// True to discover test classes which are declared internal in /// addition to test classes which are declared public. + /// to use when generating tests. /// to use when generating TestId. /// a TypeEnumerator instance. - internal virtual TypeEnumerator GetTypeEnumerator(Type type, string assemblyFileName, bool discoverInternals, TestIdGenerationStrategy testIdGenerationStrategy) + internal virtual TypeEnumerator GetTypeEnumerator(Type type, string assemblyFileName, bool discoverInternals, TestDataSourceDiscoveryOption discoveryOption, TestIdGenerationStrategy testIdGenerationStrategy) { var typeValidator = new TypeValidator(ReflectHelper, discoverInternals); var testMethodValidator = new TestMethodValidator(ReflectHelper, discoverInternals); - return new TypeEnumerator(type, assemblyFileName, ReflectHelper, typeValidator, testMethodValidator, testIdGenerationStrategy); + return new TypeEnumerator(type, assemblyFileName, ReflectHelper, typeValidator, testMethodValidator, discoveryOption, testIdGenerationStrategy); } - private List DiscoverTestsInType(string assemblyFileName, string? runSettingsXml, Type type, - List warningMessages, bool discoverInternals, TestDataSourceDiscoveryOption discoveryOption, - TestIdGenerationStrategy testIdGenerationStrategy) + private List DiscoverTestsInType( + string assemblyFileName, + [StringSyntax(StringSyntaxAttribute.Xml, nameof(runSettingsXml))] string? runSettingsXml, + Type type, + List warningMessages, + bool discoverInternals, + TestDataSourceDiscoveryOption discoveryOption, + TestIdGenerationStrategy testIdGenerationStrategy, + HashSet fixturesTests) { IDictionary tempSourceLevelParameters = PlatformServiceProvider.Instance.SettingsProvider.GetProperties(assemblyFileName); tempSourceLevelParameters = RunSettingsUtilities.GetTestRunParameters(runSettingsXml)?.ConcatWithOverwrites(tempSourceLevelParameters) @@ -217,14 +226,9 @@ private List DiscoverTestsInType(string assemblyFileName, strin try { typeFullName = type.FullName; - TypeEnumerator testTypeEnumerator = GetTypeEnumerator(type, assemblyFileName, discoverInternals, testIdGenerationStrategy); - ICollection? unitTestCases = testTypeEnumerator.Enumerate(out ICollection? warningsFromTypeEnumerator); - bool typeIgnored = ReflectHelper.IsAttributeDefined(type, false); - - if (warningsFromTypeEnumerator != null) - { - warningMessages.AddRange(warningsFromTypeEnumerator); - } + TypeEnumerator testTypeEnumerator = GetTypeEnumerator(type, assemblyFileName, discoverInternals, discoveryOption, testIdGenerationStrategy); + ICollection? unitTestCases = testTypeEnumerator.Enumerate(out ICollection warningsFromTypeEnumerator); + warningMessages.AddRange(warningsFromTypeEnumerator); if (unitTestCases != null) { @@ -232,7 +236,15 @@ private List DiscoverTestsInType(string assemblyFileName, strin { if (discoveryOption == TestDataSourceDiscoveryOption.DuringDiscovery) { - if (DynamicDataAttached(sourceLevelParameters, test, tests)) + Lazy testMethodInfo = GetTestMethodInfo(sourceLevelParameters, test); + + // Add fixture tests like AssemblyInitialize, AssemblyCleanup, ClassInitialize, ClassCleanup. + if (MSTestSettings.CurrentSettings.ConsiderFixturesAsSpecialTests && testMethodInfo.Value is not null) + { + AddFixtureTests(testMethodInfo.Value, tests, fixturesTests); + } + + if (DynamicDataAttached(test, testMethodInfo, tests)) { continue; } @@ -254,7 +266,18 @@ private List DiscoverTestsInType(string assemblyFileName, strin return tests; } - private bool DynamicDataAttached(IDictionary sourceLevelParameters, UnitTestElement test, List tests) + private Lazy GetTestMethodInfo(IDictionary sourceLevelParameters, UnitTestElement test) => + new(() => + { + // NOTE: From this place we don't have any path that would let the user write a message on the TestContext and we don't do + // anything with what would be printed anyway so we can simply use a simple StringWriter. + using var writer = new StringWriter(); + TestMethod testMethod = test.TestMethod; + MSTestAdapter.PlatformServices.Interface.ITestContext testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, sourceLevelParameters); + return _typeCache.GetTestMethodInfo(testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces); + }); + + private static bool DynamicDataAttached(UnitTestElement test, Lazy testMethodInfo, List tests) { // It should always be `true`, but if any part of the chain is obsolete; it might not contain those. // Since we depend on those properties, if they don't exist, we bail out early. @@ -263,24 +286,113 @@ private bool DynamicDataAttached(IDictionary sourceLevelParamet return false; } - // NOTE: From this place we don't have any path that would let the user write a message on the TestContext and we don't do - // anything with what would be printed anyway so we can simply use a simple StringWriter. - using var writer = new StringWriter(); - TestMethod testMethod = test.TestMethod; - MSTestAdapter.PlatformServices.Interface.ITestContext testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, sourceLevelParameters); - TestMethodInfo? testMethodInfo = _typeCache.GetTestMethodInfo(testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces); - return testMethodInfo != null && TryProcessTestDataSourceTests(test, testMethodInfo, tests); + if (test.TestMethod.DataType == DynamicDataType.None) + { + return false; + } + + // PERF: For perf we started setting DataType in TypeEnumerator, so when it is None we will not reach this line. + // But if we do run this code, we still reset it to None, because the code that determines if this is data drive test expects the value to be None + // and only sets it when needed. + // + // If you remove this line and acceptance tests still pass you are okay. + test.TestMethod.DataType = DynamicDataType.None; + + return testMethodInfo.Value != null && TryProcessTestDataSourceTests(test, testMethodInfo.Value, tests); } - private static bool TryProcessTestDataSourceTests(UnitTestElement test, TestMethodInfo testMethodInfo, List tests) + private static void AddFixtureTests(TestMethodInfo testMethodInfo, List tests, HashSet fixtureTests) { - MethodInfo methodInfo = testMethodInfo.MethodInfo; - IEnumerable? testDataSources = ReflectHelper.GetAttributes(methodInfo, false)?.OfType(); - if (testDataSources == null || !testDataSources.Any()) + string assemblyName = testMethodInfo.Parent.Parent.Assembly.GetName().Name!; + string assemblyLocation = testMethodInfo.Parent.Parent.Assembly.Location; + string className = testMethodInfo.Parent.ClassType.Name; + string classFullName = testMethodInfo.Parent.ClassType.FullName!; + + // Check if fixtures for this assembly has already been added. + if (!fixtureTests.Contains(assemblyLocation)) { - return false; + _ = fixtureTests.Add(assemblyLocation); + + // Add AssemblyInitialize and AssemblyCleanup fixture tests if they exist. + if (testMethodInfo.Parent.Parent.AssemblyInitializeMethod is not null) + { + tests.Add(GetAssemblyFixtureTest(testMethodInfo.Parent.Parent.AssemblyInitializeMethod, assemblyName, className, + classFullName, assemblyLocation, Constants.AssemblyInitializeFixtureTrait)); + } + + if (testMethodInfo.Parent.Parent.AssemblyCleanupMethod is not null) + { + tests.Add(GetAssemblyFixtureTest(testMethodInfo.Parent.Parent.AssemblyCleanupMethod, assemblyName, className, + classFullName, assemblyLocation, Constants.AssemblyCleanupFixtureTrait)); + } } + // Check if fixtures for this class has already been added. + if (!fixtureTests.Contains(assemblyLocation + classFullName)) + { + _ = fixtureTests.Add(assemblyLocation + classFullName); + + // Add ClassInitialize and ClassCleanup fixture tests if they exist. + if (testMethodInfo.Parent.ClassInitializeMethod is not null) + { + tests.Add(GetClassFixtureTest(testMethodInfo.Parent.ClassInitializeMethod, assemblyName, className, classFullName, + assemblyLocation, Constants.ClassInitializeFixtureTrait)); + } + + if (testMethodInfo.Parent.ClassCleanupMethod is not null) + { + tests.Add(GetClassFixtureTest(testMethodInfo.Parent.ClassCleanupMethod, assemblyName, className, classFullName, + assemblyLocation, Constants.ClassCleanupFixtureTrait)); + } + } + + static UnitTestElement GetAssemblyFixtureTest(MethodInfo methodInfo, string assemblyName, string className, string classFullName, + string assemblyLocation, string fixtureType) + { + string methodName = GetMethodName(methodInfo); + string[] hierarchy = [null!, assemblyName, Constants.AssemblyFixturesHierarchyClassName, methodName]; + return GetFixtureTest(classFullName, assemblyLocation, fixtureType, methodName, hierarchy); + } + + static UnitTestElement GetClassFixtureTest(MethodInfo methodInfo, string assemblyName, string className, string classFullName, + string assemblyLocation, string fixtureType) + { + string methodName = GetMethodName(methodInfo); + string[] hierarchy = [null!, classFullName, methodName]; + return GetFixtureTest(classFullName, assemblyLocation, fixtureType, methodName, hierarchy); + } + + static string GetMethodName(MethodInfo methodInfo) + { + ParameterInfo[] args = methodInfo.GetParameters(); + return args.Length > 0 + ? $"{methodInfo.Name}({string.Join(",", args.Select(a => a.ParameterType.FullName))})" + : methodInfo.Name; + } + + static UnitTestElement GetFixtureTest(string classFullName, string assemblyLocation, string fixtureType, string methodName, string[] hierarchy) + { + var method = new TestMethod(classFullName, methodName, + hierarchy, methodName, classFullName, assemblyLocation, false, + TestIdGenerationStrategy.FullyQualified); + return new UnitTestElement(method) + { + DisplayName = $"[{fixtureType}] {methodName}", + Ignored = true, + Traits = [new Trait(Constants.FixturesTestTrait, fixtureType)], + }; + } + } + + private static bool TryProcessTestDataSourceTests(UnitTestElement test, TestMethodInfo testMethodInfo, List tests) + { + MethodInfo methodInfo = testMethodInfo.MethodInfo; + + // We don't have a special method to filter attributes that are not derived from Attribute, so we take all + // attributes and filter them. We don't have to care if there is one, because this method is only entered when + // there is at least one (we determine this in TypeEnumerator.GetTestFromMethod. + IEnumerable? testDataSources = ReflectHelper.Instance.GetDerivedAttributes(methodInfo, inherit: false).OfType(); + try { return ProcessTestDataSourceTests(test, methodInfo, testDataSources, tests); @@ -298,14 +410,24 @@ private static bool ProcessTestDataSourceTests(UnitTestElement test, MethodInfo { foreach (FrameworkITestDataSource dataSource in testDataSources) { - IEnumerable? data = null; + IEnumerable? data; + + // This code is to discover tests. To run the tests code is in TestMethodRunner.ExecuteDataSourceBasedTests. + // Any change made here should be reflected in TestMethodRunner.ExecuteDataSourceBasedTests as well. try { data = dataSource.GetData(methodInfo); + + if (!data.Any()) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, FrameworkMessages.DynamicDataIEnumerableEmpty, "GetData", dataSource.GetType().Name)); + } } - catch (Exception ex) when (ex is ArgumentException && MSTestSettings.CurrentSettings.ConsiderEmptyDataSourceAsInconclusive) + catch (ArgumentException) when (MSTestSettings.CurrentSettings.ConsiderEmptyDataSourceAsInconclusive) { - var discoveredTest = test.Clone(); + UnitTestElement discoveredTest = test.Clone(); + // Make the test not data driven, because it had no data. + discoveredTest.TestMethod.DataType = DynamicDataType.None; discoveredTest.DisplayName = dataSource.GetDisplayName(methodInfo, null) ?? discoveredTest.DisplayName; tests.Add(discoveredTest); continue; diff --git a/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumeratorWrapper.cs b/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumeratorWrapper.cs index 91077657d4..5727a7e8d3 100644 --- a/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumeratorWrapper.cs +++ b/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumeratorWrapper.cs @@ -18,7 +18,7 @@ internal class AssemblyEnumeratorWrapper /// /// Assembly name for UTF. /// - private static readonly AssemblyName UnitTestFrameworkAssemblyName = typeof(TestMethodAttribute).GetTypeInfo().Assembly.GetName(); + private static readonly AssemblyName UnitTestFrameworkAssemblyName = typeof(TestMethodAttribute).Assembly.GetName(); /// /// Gets test elements from an assembly. @@ -103,7 +103,7 @@ private static ICollection GetTestsInIsolation(string fullFileP using MSTestAdapter.PlatformServices.Interface.ITestSourceHost isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(fullFilePath, runSettings, frameworkHandle: null); // Create an instance of a type defined in adapter so that adapter gets loaded in the child app domain - var assemblyEnumerator = (AssemblyEnumerator)isolationHost.CreateInstanceForType(typeof(AssemblyEnumerator), new object[] { MSTestSettings.CurrentSettings })!; + var assemblyEnumerator = (AssemblyEnumerator)isolationHost.CreateInstanceForType(typeof(AssemblyEnumerator), [MSTestSettings.CurrentSettings])!; // This might not be supported if an older version of Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices // assembly is already loaded into the App Domain. diff --git a/src/Adapter/MSTest.TestAdapter/Discovery/TestMethodValidator.cs b/src/Adapter/MSTest.TestAdapter/Discovery/TestMethodValidator.cs index eac9692d4f..861d108ab4 100644 --- a/src/Adapter/MSTest.TestAdapter/Discovery/TestMethodValidator.cs +++ b/src/Adapter/MSTest.TestAdapter/Discovery/TestMethodValidator.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Globalization; @@ -48,8 +48,14 @@ internal TestMethodValidator(ReflectHelper reflectHelper, bool discoverInternals /// Return true if a method is a valid test method. internal virtual bool IsValidTestMethod(MethodInfo testMethodInfo, Type type, ICollection warnings) { - if (!_reflectHelper.IsAttributeDefined(testMethodInfo, false) - && !_reflectHelper.HasAttributeDerivedFrom(testMethodInfo, false)) + // PERF: We are doing caching reflection here, meaning we will cache every method info in the + // assembly, this is because when we discover and run we will repeatedly inspect all the methods in the assembly, and this + // gives us a better performance. + // It would be possible to use non-caching reflection here if we knew that we are only doing discovery that won't be followed by run, + // but the difference is quite small, and we don't expect a huge amount of non-test methods in the assembly. + // + // Also skip all methods coming from object, because they cannot be tests. + if (testMethodInfo.DeclaringType == typeof(object) || !_reflectHelper.IsDerivedAttributeDefined(testMethodInfo, inherit: false)) { return false; } @@ -68,9 +74,9 @@ internal virtual bool IsValidTestMethod(MethodInfo testMethodInfo, Type type, IC // Todo: Decide whether parameter count matters. // The isGenericMethod check below id to verify that there are no closed generic methods slipping through. // Closed generic methods being GenericMethod and open being GenericMethod. - bool isValidTestMethod = isAccessible && !testMethodInfo.IsAbstract && !testMethodInfo.IsStatic - && !testMethodInfo.IsGenericMethod - && testMethodInfo.IsValidReturnType(); + bool isValidTestMethod = isAccessible && + testMethodInfo is { IsAbstract: false, IsStatic: false, IsGenericMethod: false } && + testMethodInfo.IsValidReturnType(); if (!isValidTestMethod) { diff --git a/src/Adapter/MSTest.TestAdapter/Discovery/TypeEnumerator.cs b/src/Adapter/MSTest.TestAdapter/Discovery/TypeEnumerator.cs index 0a1097105f..e0fab8b22e 100644 --- a/src/Adapter/MSTest.TestAdapter/Discovery/TypeEnumerator.cs +++ b/src/Adapter/MSTest.TestAdapter/Discovery/TypeEnumerator.cs @@ -22,6 +22,7 @@ internal class TypeEnumerator private readonly TypeValidator _typeValidator; private readonly TestMethodValidator _testMethodValidator; private readonly TestIdGenerationStrategy _testIdGenerationStrategy; + private readonly TestDataSourceDiscoveryOption _discoveryOption; private readonly ReflectHelper _reflectHelper; /// @@ -33,7 +34,7 @@ internal class TypeEnumerator /// The validator for test classes. /// The validator for test methods. /// to use when generating TestId. - internal TypeEnumerator(Type type, string assemblyFilePath, ReflectHelper reflectHelper, TypeValidator typeValidator, TestMethodValidator testMethodValidator, TestIdGenerationStrategy testIdGenerationStrategy) + internal TypeEnumerator(Type type, string assemblyFilePath, ReflectHelper reflectHelper, TypeValidator typeValidator, TestMethodValidator testMethodValidator, TestDataSourceDiscoveryOption discoveryOption, TestIdGenerationStrategy testIdGenerationStrategy) { _type = type; _assemblyFilePath = assemblyFilePath; @@ -41,6 +42,7 @@ internal TypeEnumerator(Type type, string assemblyFilePath, ReflectHelper reflec _typeValidator = typeValidator; _testMethodValidator = testMethodValidator; _testIdGenerationStrategy = testIdGenerationStrategy; + _discoveryOption = discoveryOption; } /// @@ -73,6 +75,8 @@ internal Collection GetTests(ICollection warnings) var tests = new Collection(); // Test class is already valid. Verify methods. + // PERF: GetRuntimeMethods is used here to get all methods, including non-public, and static methods. + // if we rely on analyzers to identify all invalid methods on build, we can change this to fit the current settings. foreach (MethodInfo method in _type.GetRuntimeMethods()) { bool isMethodDeclaredInTestTypeAssembly = _reflectHelper.IsMethodDeclaredInSameAssemblyAsType(method, _type); @@ -107,7 +111,7 @@ internal Collection GetTests(ICollection warnings) { inheritanceDepths[currentType.FullName!] = currentDepth; ++currentDepth; - currentType = currentType.GetTypeInfo().BaseType; + currentType = currentType.BaseType; } return new Collection( @@ -144,17 +148,33 @@ internal UnitTestElement GetTestFromMethod(MethodInfo method, bool isDeclaredInT { testMethod.DeclaringAssemblyName = PlatformServiceProvider.Instance.FileOperations.GetAssemblyPath( - method.DeclaringType.GetTypeInfo().Assembly); + method.DeclaringType.Assembly); + } + + // PERF: When discovery option is set to DuringDiscovery, we will expand data on tests to one test case + // per data item. This will happen in AssemblyEnumerator. But AssemblyEnumerator does not have direct access to + // the method info or method attributes, so it would create a TestMethodInfo to see if the test is data driven. + // Creating TestMethodInfo is expensive and should be done only for a test that we know is data driven. + // + // So to optimize this we check if we have some data source attribute. Because here we have access to all attributes + // and we store that info in DataType. AssemblyEnumerator will pick this up and will get the real test data in the expensive way + // or it will skip over getting the data cheaply, when DataType = DynamicDataType.None. + // + // This needs to be done only when DuringDiscovery is set, because otherwise we would populate the DataType, but we would not populate + // and execution would not try to re-populate the data, because DataType is already set to data driven, so it would just throw error about empty data. + if (_discoveryOption == TestDataSourceDiscoveryOption.DuringDiscovery) + { + testMethod.DataType = GetDynamicDataType(method); } var testElement = new UnitTestElement(testMethod) { // Get compiler generated type name for async test method (either void returning or task returning). AsyncTypeName = method.GetAsyncTypeName(), - TestCategory = _reflectHelper.GetCategories(method, _type), + TestCategory = _reflectHelper.GetTestCategories(method, _type), DoNotParallelize = _reflectHelper.IsDoNotParallelizeSet(method, _type), Priority = _reflectHelper.GetPriority(method), - Ignored = _reflectHelper.IsAttributeDefined(method, false), + Ignored = _reflectHelper.IsNonDerivedAttributeDefined(method, inherit: false), DeploymentItems = PlatformServiceProvider.Instance.TestDeployment.GetDeploymentItems(method, _type, warnings), }; @@ -174,31 +194,49 @@ internal UnitTestElement GetTestFromMethod(MethodInfo method, bool isDeclaredInT testElement.Traits = traits.ToArray(); - if (_reflectHelper.GetCustomAttribute(method) is CssIterationAttribute cssIteration) + if (_reflectHelper.GetFirstDerivedAttributeOrDefault(method, inherit: true) is CssIterationAttribute cssIteration) { testElement.CssIteration = cssIteration.CssIteration; } - if (_reflectHelper.GetCustomAttribute(method) is CssProjectStructureAttribute cssProjectStructure) + if (_reflectHelper.GetFirstDerivedAttributeOrDefault(method, inherit: true) is CssProjectStructureAttribute cssProjectStructure) { testElement.CssProjectStructure = cssProjectStructure.CssProjectStructure; } - if (_reflectHelper.GetCustomAttribute(method) is DescriptionAttribute descriptionAttribute) + if (_reflectHelper.GetFirstDerivedAttributeOrDefault(method, inherit: true) is DescriptionAttribute descriptionAttribute) { testElement.Description = descriptionAttribute.Description; } - WorkItemAttribute[] workItemAttributes = _reflectHelper.GetCustomAttributes(method); + WorkItemAttribute[] workItemAttributes = _reflectHelper.GetDerivedAttributes(method, inherit: true).ToArray(); if (workItemAttributes.Length != 0) { testElement.WorkItemIds = workItemAttributes.Select(x => x.Id.ToString(CultureInfo.InvariantCulture)).ToArray(); } // get DisplayName from TestMethodAttribute (or any inherited attribute) - TestMethodAttribute? testMethodAttribute = _reflectHelper.GetCustomAttribute(method); + TestMethodAttribute? testMethodAttribute = _reflectHelper.GetFirstDerivedAttributeOrDefault(method, inherit: true); testElement.DisplayName = testMethodAttribute?.DisplayName ?? method.Name; return testElement; } + + private DynamicDataType GetDynamicDataType(MethodInfo method) + { + foreach (Attribute attribute in _reflectHelper.GetDerivedAttributes(method, inherit: true)) + { + if (AttributeComparer.IsDerived(attribute)) + { + return DynamicDataType.ITestDataSource; + } + + if (AttributeComparer.IsDerived(attribute)) + { + return DynamicDataType.DataSourceAttribute; + } + } + + return DynamicDataType.None; + } } diff --git a/src/Adapter/MSTest.TestAdapter/Discovery/TypeValidator.cs b/src/Adapter/MSTest.TestAdapter/Discovery/TypeValidator.cs index 991d6ade55..edea385520 100644 --- a/src/Adapter/MSTest.TestAdapter/Discovery/TypeValidator.cs +++ b/src/Adapter/MSTest.TestAdapter/Discovery/TypeValidator.cs @@ -51,9 +51,12 @@ internal virtual bool IsValidTestClass(Type type, ICollection warnings) { TypeInfo typeInfo = type.GetTypeInfo(); - if (!typeInfo.IsClass - || (!_reflectHelper.IsAttributeDefined(typeInfo, false) - && !_reflectHelper.HasAttributeDerivedFrom(typeInfo, false))) + // PERF: We are doing caching reflection here, meaning we will cache every class info in the + // assembly, this is because when we discover and run we will repeatedly inspect all the types in the assembly, and this + // gives us a better performance. + // It would be possible to use non-caching reflection here if we knew that we are only doing discovery that won't be followed by run, + // but the difference is quite small, and we don't expect a huge amount of non-test classes in the assembly. + if (!typeInfo.IsClass || !_reflectHelper.IsDerivedAttributeDefined(typeInfo, inherit: false)) { return false; } @@ -67,7 +70,7 @@ internal virtual bool IsValidTestClass(Type type, ICollection warnings) } // Generic class - if (typeInfo.IsGenericTypeDefinition && !typeInfo.IsAbstract) + if (typeInfo is { IsGenericTypeDefinition: true, IsAbstract: false }) { // In IDE generic classes that are not abstract are treated as not runnable. Keep consistence. string warning = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorTestClassIsGenericNonAbstract, type.FullName); @@ -192,7 +195,7 @@ internal static bool TypeHasValidAccessibility(TypeInfo type, bool discoverInter // Or the type is nested internal, or nested public type, but not any other // like nested protected internal type, or nested private type. - || declaringType.GetTypeInfo().IsNestedAssembly || declaringType.GetTypeInfo().IsNestedPublic; + || declaringType.IsNestedAssembly || declaringType.GetTypeInfo().IsNestedPublic; if (!declaringTypeIsPublicOrInternal) { diff --git a/src/Adapter/MSTest.TestAdapter/Discovery/UnitTestDiscoverer.cs b/src/Adapter/MSTest.TestAdapter/Discovery/UnitTestDiscoverer.cs index 7b51db7095..1bd7d61b5d 100644 --- a/src/Adapter/MSTest.TestAdapter/Discovery/UnitTestDiscoverer.cs +++ b/src/Adapter/MSTest.TestAdapter/Discovery/UnitTestDiscoverer.cs @@ -1,10 +1,11 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Globalization; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -90,6 +91,8 @@ internal virtual void DiscoverTestsInSource( internal void SendTestCases(string source, IEnumerable testElements, ITestCaseDiscoverySink discoverySink, IDiscoveryContext? discoveryContext, IMessageLogger logger) { bool shouldCollectSourceInformation = MSTestSettings.RunConfigurationSettings.CollectSourceInformation; + bool hasAnyRunnableTests = false; + var fixtureTests = new List(); var navigationSessions = new Dictionary(); try @@ -109,13 +112,25 @@ internal void SendTestCases(string source, IEnumerable testElem foreach (UnitTestElement testElement in testElements) { var testCase = testElement.ToTestCase(); + bool hasFixtureTraits = testCase.Traits.Any(t => t.Name == Constants.FixturesTestTrait); // Filter tests based on test case filters if (filterExpression != null && !filterExpression.MatchTestCase(testCase, (p) => TestMethodFilter.PropertyValueProvider(testCase, p))) { + // If test is a fixture test, add it to the list of fixture tests. + if (hasFixtureTraits) + { + fixtureTests.Add(testCase); + } + continue; } + if (!hasAnyRunnableTests) + { + hasAnyRunnableTests = !hasFixtureTraits; + } + if (!shouldCollectSourceInformation) { discoverySink.SendTestCase(testCase); @@ -165,6 +180,18 @@ internal void SendTestCases(string source, IEnumerable testElem discoverySink.SendTestCase(testCase); } + + // If there are runnable tests, then add all fixture tests to the discovery sink. + // Scenarios: + // 1. Execute only a fixture test => In this case, we do not need to track any other fixture tests. Selected fixture test will be tracked as will be marked as skipped. + // 2. Execute a runnable test => In this case, case add all fixture tests. We will update status of only those fixtures which are triggered by the selected test. + if (hasAnyRunnableTests) + { + foreach (TestCase testCase in fixtureTests) + { + discoverySink.SendTestCase(testCase); + } + } } finally { diff --git a/src/Adapter/MSTest.TestAdapter/Execution/LogMessageListener.cs b/src/Adapter/MSTest.TestAdapter/Execution/LogMessageListener.cs index 5e75ec1d63..522356c9fa 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/LogMessageListener.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/LogMessageListener.cs @@ -119,13 +119,7 @@ public LogMessageListener(bool captureDebugTraces) [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Part of the public API")] public string? GetAndClearDebugTrace() { - if (s_redirectedDebugTrace == null) - { - return null; - } - - string? output = s_redirectedDebugTrace.ToStringAndClear(); - return output; + return s_redirectedDebugTrace?.ToStringAndClear(); } public void Dispose() diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestAssemblyInfo.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestAssemblyInfo.cs index 6b40113939..bfe177d675 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestAssemblyInfo.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestAssemblyInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Diagnostics.CodeAnalysis; @@ -8,6 +8,7 @@ using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestTools.UnitTesting; using UnitTestOutcome = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome; @@ -94,6 +95,11 @@ internal set /// public Exception? AssemblyInitializationException { get; internal set; } + /// + /// Gets the assembly cleanup exception. + /// + internal Exception? AssemblyCleanupException { get; private set; } + /// /// Gets a value indicating whether this assembly has an executable AssemblyCleanup method. /// @@ -140,11 +146,12 @@ public void RunAssemblyInitialize(TestContext testContext) { try { - AssemblyInitializationException = MethodRunner.RunWithTimeoutAndCancellation( + AssemblyInitializationException = FixtureMethodRunner.RunWithTimeoutAndCancellation( () => AssemblyInitializeMethod.InvokeAsSynchronousTask(null, testContext), testContext.CancellationTokenSource, AssemblyInitializeMethodTimeoutMilliseconds, AssemblyInitializeMethod, + new AssemblyExecutionContextScope(isCleanup: false), Resource.AssemblyInitializeWasCancelled, Resource.AssemblyInitializeTimedOut); } @@ -208,32 +215,32 @@ public void RunAssemblyInitialize(TestContext testContext) return null; } - Exception? assemblyCleanupException = null; lock (_assemblyInfoExecuteSyncObject) { try { - assemblyCleanupException = MethodRunner.RunWithTimeoutAndCancellation( + AssemblyCleanupException = FixtureMethodRunner.RunWithTimeoutAndCancellation( () => AssemblyCleanupMethod.InvokeAsSynchronousTask(null), new CancellationTokenSource(), AssemblyCleanupMethodTimeoutMilliseconds, AssemblyCleanupMethod, + new AssemblyExecutionContextScope(isCleanup: true), Resource.AssemblyCleanupWasCancelled, Resource.AssemblyCleanupTimedOut); } catch (Exception ex) { - assemblyCleanupException = ex; + AssemblyCleanupException = ex; } } // If assemblyCleanup was successful, then don't do anything - if (assemblyCleanupException is null) + if (AssemblyCleanupException is null) { return null; } - Exception realException = assemblyCleanupException.GetRealException(); + Exception realException = AssemblyCleanupException.GetRealException(); // special case AssertFailedException to trim off part of the stack trace string errorMessage = realException is AssertFailedException or AssertInconclusiveException @@ -264,38 +271,38 @@ internal void ExecuteAssemblyCleanup() return; } - Exception? assemblyCleanupException; lock (_assemblyInfoExecuteSyncObject) { try { - assemblyCleanupException = MethodRunner.RunWithTimeoutAndCancellation( + AssemblyCleanupException = FixtureMethodRunner.RunWithTimeoutAndCancellation( () => AssemblyCleanupMethod.InvokeAsSynchronousTask(null), new CancellationTokenSource(), AssemblyCleanupMethodTimeoutMilliseconds, AssemblyCleanupMethod, + new AssemblyExecutionContextScope(isCleanup: true), Resource.AssemblyCleanupWasCancelled, Resource.AssemblyCleanupTimedOut); } catch (Exception ex) { - assemblyCleanupException = ex; + AssemblyCleanupException = ex; } } // If assemblyCleanup was successful, then don't do anything - if (assemblyCleanupException is null) + if (AssemblyCleanupException is null) { return; } // If the exception is already a `TestFailedException` we throw it as-is - if (assemblyCleanupException is TestFailedException) + if (AssemblyCleanupException is TestFailedException) { - throw assemblyCleanupException; + throw AssemblyCleanupException; } - Exception realException = assemblyCleanupException.GetRealException(); + Exception realException = AssemblyCleanupException.GetRealException(); // special case AssertFailedException to trim off part of the stack trace string errorMessage = realException is AssertFailedException or AssertInconclusiveException diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestClassInfo.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestClassInfo.cs index fd33c38a19..ef314d0d2f 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestClassInfo.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestClassInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Diagnostics.CodeAnalysis; @@ -8,6 +8,7 @@ using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestTools.UnitTesting; using ObjectModelUnitTestOutcome = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome; @@ -363,11 +364,12 @@ public void RunClassInitialize(TestContext testContext) timeout = localTimeout; } - return MethodRunner.RunWithTimeoutAndCancellation( + return FixtureMethodRunner.RunWithTimeoutAndCancellation( () => methodInfo.InvokeAsSynchronousTask(null, testContext), testContext.CancellationTokenSource, timeout, methodInfo, + new ClassExecutionContextScope(ClassType), Resource.ClassInitializeWasCancelled, Resource.ClassInitializeTimedOut); } @@ -404,12 +406,12 @@ public void RunClassInitialize(TestContext testContext) try { classCleanupMethod = ClassCleanupMethod; - ClassCleanupException = classCleanupMethod is not null ? InvokeCleanupMethod(classCleanupMethod) : null; + ClassCleanupException = classCleanupMethod is not null ? InvokeCleanupMethod(classCleanupMethod, BaseClassCleanupMethodsStack.Count) : null; var baseClassCleanupQueue = new Queue(BaseClassCleanupMethodsStack); while (baseClassCleanupQueue.Count > 0 && ClassCleanupException is null) { classCleanupMethod = baseClassCleanupQueue.Dequeue(); - ClassCleanupException = classCleanupMethod is not null ? InvokeCleanupMethod(classCleanupMethod) : null; + ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, baseClassCleanupQueue.Count); } IsClassCleanupExecuted = ClassCleanupException is null; @@ -474,6 +476,7 @@ internal void ExecuteClassCleanup() lock (_testClassExecuteSyncObject) { if (IsClassCleanupExecuted + // If there is a ClassInitialize method and it has not been executed, then we should not execute ClassCleanup || (!IsClassInitializeExecuted && ClassInitializeMethod is not null)) { return; @@ -481,13 +484,16 @@ internal void ExecuteClassCleanup() try { - classCleanupMethod = ClassCleanupMethod; - ClassCleanupException = classCleanupMethod is not null ? InvokeCleanupMethod(classCleanupMethod) : null; - var baseClassCleanupQueue = new Queue(BaseClassCleanupMethodsStack); - while (baseClassCleanupQueue.Count > 0 && ClassCleanupException is null) + IEnumerable cleanupMethods = (ClassCleanupMethod is null ? Array.Empty() : [ClassCleanupMethod]).Union(BaseClassCleanupMethodsStack); + var classCleanupQueue = new Queue(cleanupMethods); + + while (classCleanupQueue.Count > 0 && ClassCleanupException is null) { - classCleanupMethod = baseClassCleanupQueue.Dequeue(); - ClassCleanupException = classCleanupMethod is not null ? InvokeCleanupMethod(classCleanupMethod) : null; + classCleanupMethod = classCleanupQueue.Dequeue(); + if (!ReflectHelper.Instance.IsNonDerivedAttributeDefined(classCleanupMethod.DeclaringType!, false)) + { + ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, classCleanupQueue.Count); + } } IsClassCleanupExecuted = ClassCleanupException is null; @@ -535,7 +541,7 @@ internal void ExecuteClassCleanup() throw testFailedException; } - private TestFailedException? InvokeCleanupMethod(MethodInfo methodInfo) + private TestFailedException? InvokeCleanupMethod(MethodInfo methodInfo, int remainingCleanupCount) { int? timeout = null; if (ClassCleanupMethodTimeoutMilliseconds.TryGetValue(methodInfo, out int localTimeout)) @@ -543,11 +549,12 @@ internal void ExecuteClassCleanup() timeout = localTimeout; } - return MethodRunner.RunWithTimeoutAndCancellation( + return FixtureMethodRunner.RunWithTimeoutAndCancellation( () => methodInfo.InvokeAsSynchronousTask(null), new CancellationTokenSource(), timeout, methodInfo, + new ClassExecutionContextScope(ClassType, remainingCleanupCount), Resource.ClassCleanupWasCancelled, Resource.ClassCleanupTimedOut); } diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestExecutionManager.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestExecutionManager.cs index 4bb000deb4..bae78566bd 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestExecutionManager.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestExecutionManager.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Runtime.InteropServices; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; @@ -42,12 +43,36 @@ internal TestExecutionManager(IEnvironment environment, Func? task TestMethodFilter = new TestMethodFilter(); _sessionParameters = new Dictionary(); _environment = environment; - _taskFactory = taskFactory - ?? (action => Task.Factory.StartNew( - action, - CancellationToken.None, - TaskCreationOptions.LongRunning, - TaskScheduler.Default)); + _taskFactory = taskFactory ?? DefaultFactoryAsync; + } + + private static Task DefaultFactoryAsync(Action action) + { + if (MSTestSettings.RunConfigurationSettings.ExecutionApartmentState == ApartmentState.STA + && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + TaskCompletionSource tcs = new(); + Thread entryPointThread = new(() => + { + try + { + action(); + tcs.SetResult(0); + } + catch (Exception ex) + { + tcs.SetException(ex); + } + }); + + entryPointThread.SetApartmentState(ApartmentState.STA); + entryPointThread.Start(); + return tcs.Task; + } + else + { + return Task.Run(action); + } } /// @@ -178,7 +203,8 @@ internal void SendTestResults(TestCase test, IEnumerable unitTes try { - if (testResult.Outcome != TestOutcome.NotFound || !IsInHotReloadMode()) + if (testResult.Outcome != TestOutcome.NotFound + || !RuntimeContext.IsHotReloadEnabled) { testExecutionRecorder.RecordResult(testResult); } @@ -190,16 +216,17 @@ internal void SendTestResults(TestCase test, IEnumerable unitTes } } - // TODO: We should be using a capability from the runner instead of looking at environment variables. - private static bool IsInHotReloadMode() - => Environment.GetEnvironmentVariable("DOTNET_WATCH") == "1" - || Environment.GetEnvironmentVariable("TESTINGPLATFORM_HOTRELOAD_ENABLED") == "1"; - private static bool MatchTestFilter(ITestCaseFilterExpression? filterExpression, TestCase test, TestMethodFilter testMethodFilter) { if (filterExpression != null && !filterExpression.MatchTestCase(test, p => testMethodFilter.PropertyValueProvider(test, p))) { + // If this is a fixture test, return true. Fixture tests are not filtered out and are always available for the status. + if (test.Traits.Any(t => t.Name == Constants.FixturesTestTrait)) + { + return true; + } + // Skip test if not fitting filter criteria. return false; } @@ -229,7 +256,7 @@ private void ExecuteTestsInSource(IEnumerable tests, IRunContext? runC // Create an instance of a type defined in adapter so that adapter gets loaded in the child app domain var testRunner = (UnitTestRunner)isolationHost.CreateInstanceForType( typeof(UnitTestRunner), - new object[] { MSTestSettings.CurrentSettings })!; + [MSTestSettings.CurrentSettings])!; PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Created unit-test runner {0}", source); @@ -245,7 +272,7 @@ private void ExecuteTestsInSource(IEnumerable tests, IRunContext? runC // and are merged with session level parameters IDictionary sourceLevelParameters = PlatformServiceProvider.Instance.SettingsProvider.GetProperties(source); - if (_sessionParameters != null && _sessionParameters.Count > 0) + if (_sessionParameters is { Count: > 0 }) { sourceLevelParameters = _sessionParameters.ConcatWithOverwrites(sourceLevelParameters); } @@ -284,16 +311,13 @@ private void ExecuteTestsInSource(IEnumerable tests, IRunContext? runC if (!MSTestSettings.CurrentSettings.DisableParallelization && sourceSettings.CanParallelizeAssembly && parallelWorkers > 0) { // Parallelization is enabled. Let's do further classification for sets. - var logger = (IMessageLogger)frameworkHandle; - logger.SendMessage( + frameworkHandle.SendMessage( TestMessageLevel.Informational, string.Format(CultureInfo.CurrentCulture, Resource.TestParallelizationBanner, source, parallelWorkers, parallelScope)); // Create test sets for execution, we can execute them in parallel based on parallel settings - IEnumerable> testSets = Enumerable.Empty>(); - // Parallel and not parallel sets. - testSets = testsToRun.GroupBy(t => t.GetPropertyValue(TestAdapter.Constants.DoNotParallelizeProperty, false)); + IEnumerable> testSets = testsToRun.GroupBy(t => t.GetPropertyValue(Constants.DoNotParallelizeProperty, false)); IGrouping? parallelizableTestSet = testSets.FirstOrDefault(g => !g.Key); IGrouping? nonParallelizableTestSet = testSets.FirstOrDefault(g => g.Key); @@ -310,7 +334,7 @@ private void ExecuteTestsInSource(IEnumerable tests, IRunContext? runC break; case ExecutionScope.ClassLevel: - queue = new ConcurrentQueue>(parallelizableTestSet.GroupBy(t => t.GetPropertyValue(TestAdapter.Constants.TestClassNameProperty) as string)); + queue = new ConcurrentQueue>(parallelizableTestSet.GroupBy(t => t.GetPropertyValue(Constants.TestClassNameProperty) as string)); break; } @@ -322,7 +346,7 @@ private void ExecuteTestsInSource(IEnumerable tests, IRunContext? runC { while (!queue!.IsEmpty) { - if (_cancellationToken != null && _cancellationToken.Canceled) + if (_cancellationToken is { Canceled: true }) { // if a cancellation has been requested, do not queue any more test runs. break; @@ -336,7 +360,17 @@ private void ExecuteTestsInSource(IEnumerable tests, IRunContext? runC })); } - Task.WaitAll(tasks.ToArray()); + try + { + Task.WaitAll(tasks.ToArray()); + } + catch (Exception ex) + { + string exceptionToString = ex.ToString(); + PlatformServiceProvider.Instance.AdapterTraceLogger.LogError("Error occurred while executing tests in parallel{0}{1}", Environment.NewLine, exceptionToString); + frameworkHandle.SendMessage(TestMessageLevel.Error, exceptionToString); + throw; + } } // Queue the non parallel set @@ -379,13 +413,25 @@ private void ExecuteTestsWithTestRunner( IDictionary sourceLevelParameters, UnitTestRunner testRunner) { + bool hasAnyRunnableTests = false; + var fixtureTests = new List(); + foreach (TestCase currentTest in tests) { - if (_cancellationToken != null && _cancellationToken.Canceled) + if (_cancellationToken is { Canceled: true }) { break; } + // If it is a fixture test, add it to the list of fixture tests and do not execute it. + // It is executed by test itself. + if (currentTest.Traits.Any(t => t.Name == Constants.FixturesTestTrait)) + { + fixtureTests.Add(currentTest); + continue; + } + + hasAnyRunnableTests = true; var unitTestElement = currentTest.ToUnitTestElement(source); testExecutionRecorder.RecordStart(currentTest); @@ -405,6 +451,30 @@ private void ExecuteTestsWithTestRunner( SendTestResults(currentTest, unitTestResult, startTime, endTime, testExecutionRecorder); } + + // Once all tests have been executed, update the status of fixture tests. + foreach (TestCase currentTest in fixtureTests) + { + testExecutionRecorder.RecordStart(currentTest); + + // If there were only fixture tests, send an inconclusive result. + if (!hasAnyRunnableTests) + { + var result = new UnitTestResult(ObjectModel.UnitTestOutcome.Inconclusive, null); + SendTestResults(currentTest, [result], DateTimeOffset.Now, DateTimeOffset.Now, testExecutionRecorder); + continue; + } + + Trait trait = currentTest.Traits.First(t => t.Name == Constants.FixturesTestTrait); + var unitTestElement = currentTest.ToUnitTestElement(source); + FixtureTestResult fixtureTestResult = testRunner.GetFixtureTestResult(unitTestElement.TestMethod, trait.Value); + + if (fixtureTestResult.IsExecuted) + { + var result = new UnitTestResult(fixtureTestResult.Outcome, null); + SendTestResults(currentTest, [result], DateTimeOffset.Now, DateTimeOffset.Now, testExecutionRecorder); + } + } } /// @@ -444,7 +514,7 @@ private void CacheSessionParameters(IRunContext? runContext, ITestExecutionRecor try { - Dictionary testRunParameters = RunSettingsUtilities.GetTestRunParameters(runContext.RunSettings.SettingsXml); + Dictionary? testRunParameters = RunSettingsUtilities.GetTestRunParameters(runContext.RunSettings.SettingsXml); if (testRunParameters != null) { // Clear sessionParameters to prevent key collisions of test run parameters in case diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestMethodInfo.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestMethodInfo.cs index 1be63d983e..f0d5f65865 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestMethodInfo.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestMethodInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Diagnostics; @@ -10,6 +10,7 @@ using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestTools.UnitTesting; using ObjectModelUnitTestOutcome = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome; @@ -82,12 +83,11 @@ internal TestMethodInfo( /// internal TestMethodOptions TestMethodOptions { get; } - public Attribute[]? GetAllAttributes(bool inherit) => ReflectHelper.GetCustomAttributes(TestMethod, inherit) as Attribute[]; + public Attribute[]? GetAllAttributes(bool inherit) => ReflectHelper.Instance.GetDerivedAttributes(TestMethod, inherit).ToArray(); public TAttributeType[] GetAttributes(bool inherit) where TAttributeType : Attribute - => ReflectHelper.GetAttributes(TestMethod, inherit) - ?? Array.Empty(); + => ReflectHelper.Instance.GetDerivedAttributes(TestMethod, inherit).ToArray(); /// /// Execute test method. Capture failures, handle async and return result. @@ -104,27 +104,25 @@ public virtual TestResult Invoke(object?[]? arguments) // check if arguments are set for data driven tests arguments ??= Arguments; - using (LogMessageListener listener = new(TestMethodOptions.CaptureDebugTraces)) + using LogMessageListener listener = new(TestMethodOptions.CaptureDebugTraces); + watch.Start(); + try { - watch.Start(); - try - { - result = IsTimeoutSet ? ExecuteInternalWithTimeout(arguments) : ExecuteInternal(arguments); - } - finally - { - // Handle logs & debug traces. - watch.Stop(); + result = IsTimeoutSet ? ExecuteInternalWithTimeout(arguments) : ExecuteInternal(arguments); + } + finally + { + // Handle logs & debug traces. + watch.Stop(); - if (result != null) - { - result.Duration = watch.Elapsed; - result.DebugTrace = listener.GetAndClearDebugTrace(); - result.LogOutput = listener.GetAndClearStandardOutput(); - result.LogError = listener.GetAndClearStandardError(); - result.TestContextMessages = TestMethodOptions.TestContext?.GetAndClearDiagnosticMessages(); - result.ResultFiles = TestMethodOptions.TestContext?.GetResultFiles(); - } + if (result != null) + { + result.Duration = watch.Elapsed; + result.DebugTrace = listener.GetAndClearDebugTrace(); + result.LogOutput = listener.GetAndClearStandardOutput(); + result.LogError = listener.GetAndClearStandardError(); + result.TestContextMessages = TestMethodOptions.TestContext?.GetAndClearDiagnosticMessages(); + result.ResultFiles = TestMethodOptions.TestContext?.GetResultFiles(); } } @@ -177,7 +175,7 @@ public virtual TestResult Invoke(object?[]? arguments) // If this is the params parameter, instantiate a new object of that type if (argumentIndex == parametersInfo.Length - 1) { - paramsValues = Activator.CreateInstance(parametersInfo[argumentIndex].ParameterType, new object[] { arguments.Length - argumentIndex }); + paramsValues = Activator.CreateInstance(parametersInfo[argumentIndex].ParameterType, [arguments.Length - argumentIndex]); newParameters[argumentIndex] = paramsValues; } @@ -239,7 +237,17 @@ private TestResult ExecuteInternal(object?[]? arguments) if (RunTestInitializeMethod(classInstance, result)) { hasTestInitializePassed = true; - TestMethod.InvokeAsSynchronousTask(classInstance, arguments); + if (IsTimeoutSet) + { + ExecutionContextService.RunActionOnContext( + () => TestMethod.InvokeAsSynchronousTask(classInstance, arguments), + new InstanceExecutionContextScope(classInstance, Parent.ClassType)); + } + else + { + TestMethod.InvokeAsSynchronousTask(classInstance, arguments); + } + result.Outcome = UTF.UnitTestOutcome.Passed; } } @@ -249,31 +257,23 @@ private TestResult ExecuteInternal(object?[]? arguments) isExceptionThrown = true; Exception realException = GetRealException(ex); - if (realException is MissingMethodException) + if (IsExpectedException(realException, result)) { - result.Outcome = UTF.UnitTestOutcome.NotFound; - result.TestFailureException = realException; + // Expected Exception was thrown, so Pass the test + result.Outcome = UTF.UnitTestOutcome.Passed; } else { - if (IsExpectedException(realException, result)) - { - // Expected Exception was thrown, so Pass the test - result.Outcome = UTF.UnitTestOutcome.Passed; - } - else - { - // This block should not throw. If it needs to throw, then handling of - // ThreadAbortException will need to be revisited. See comment in RunTestMethod. - result.TestFailureException ??= HandleMethodException(ex, realException, TestClassName, TestMethodName); - } + // This block should not throw. If it needs to throw, then handling of + // ThreadAbortException will need to be revisited. See comment in RunTestMethod. + result.TestFailureException ??= HandleMethodException(ex, realException, TestClassName, TestMethodName); + } - if (result.Outcome != UTF.UnitTestOutcome.Passed) - { - result.Outcome = ex is AssertInconclusiveException || ex.InnerException is AssertInconclusiveException - ? UTF.UnitTestOutcome.Inconclusive - : UTF.UnitTestOutcome.Failed; - } + if (result.Outcome != UTF.UnitTestOutcome.Passed) + { + result.Outcome = ex is AssertInconclusiveException || ex.InnerException is AssertInconclusiveException + ? UTF.UnitTestOutcome.Inconclusive + : UTF.UnitTestOutcome.Failed; } } @@ -396,6 +396,19 @@ private static TestFailedException HandleMethodException(Exception ex, Exception return testFailedException; } + // If we are in hot reload context and the exception is a MissingMethodException and the first line of the stack + // trace contains the method name then it's likely that the current method was removed and the test is failing. + // For cases where the content of the test would throw a MissingMethodException, the first line of the stack trace + // would not be the test method name, so we can safely assume this is a proper test failure. + if (ex is MissingMethodException missingMethodException + && RuntimeContext.IsHotReloadEnabled + && missingMethodException.StackTrace?.IndexOf(Environment.NewLine, StringComparison.Ordinal) is { } lineReturnIndex + && lineReturnIndex >= 0 + && missingMethodException.StackTrace.Substring(0, lineReturnIndex).Contains($"{className}.{methodName}")) + { + return new TestFailedException(ObjectModelUnitTestOutcome.NotFound, missingMethodException.Message, missingMethodException); + } + // Get the real exception thrown by the test method if (realException.TryGetUnitTestAssertException(out UTF.UnitTestOutcome outcome, out string? exceptionMessage, out StackTraceInformation? exceptionStackTraceInfo)) { @@ -448,12 +461,14 @@ private void RunTestCleanupMethod(object classInstance, TestResult result) { // Test cleanups are called in the order of discovery // Current TestClass -> Parent -> Grandparent - testCleanupException = testCleanupMethod is not null ? InvokeCleanupMethod(testCleanupMethod, classInstance) : null; + testCleanupException = testCleanupMethod is not null + ? InvokeCleanupMethod(testCleanupMethod, classInstance, Parent.BaseTestCleanupMethodsQueue.Count) + : null; var baseTestCleanupQueue = new Queue(Parent.BaseTestCleanupMethodsQueue); while (baseTestCleanupQueue.Count > 0 && testCleanupException is null) { testCleanupMethod = baseTestCleanupQueue.Dequeue(); - testCleanupException = testCleanupMethod is not null ? InvokeCleanupMethod(testCleanupMethod, classInstance) : null; + testCleanupException = InvokeCleanupMethod(testCleanupMethod, classInstance, baseTestCleanupQueue.Count); } } finally @@ -635,16 +650,17 @@ private bool RunTestInitializeMethod(object classInstance, TestResult result) timeout = localTimeout; } - return MethodRunner.RunWithTimeoutAndCancellation( + return FixtureMethodRunner.RunWithTimeoutAndCancellation( () => methodInfo.InvokeAsSynchronousTask(classInstance, null), - new CancellationTokenSource(), + TestMethodOptions.TestContext!.Context.CancellationTokenSource, timeout, methodInfo, + new InstanceExecutionContextScope(classInstance, Parent.ClassType), Resource.TestInitializeWasCancelled, Resource.TestInitializeTimedOut); } - private TestFailedException? InvokeCleanupMethod(MethodInfo methodInfo, object classInstance) + private TestFailedException? InvokeCleanupMethod(MethodInfo methodInfo, object classInstance, int remainingCleanupCount) { int? timeout = null; if (Parent.TestCleanupMethodTimeoutMilliseconds.TryGetValue(methodInfo, out int localTimeout)) @@ -652,11 +668,12 @@ private bool RunTestInitializeMethod(object classInstance, TestResult result) timeout = localTimeout; } - return MethodRunner.RunWithTimeoutAndCancellation( + return FixtureMethodRunner.RunWithTimeoutAndCancellation( () => methodInfo.InvokeAsSynchronousTask(classInstance, null), - new CancellationTokenSource(), + TestMethodOptions.TestContext!.Context.CancellationTokenSource, timeout, methodInfo, + new InstanceExecutionContextScope(classInstance, Parent.ClassType, remainingCleanupCount), Resource.TestCleanupWasCancelled, Resource.TestCleanupTimedOut); } @@ -784,7 +801,7 @@ void ExecuteAsyncAction() } } - CancellationToken cancelToken = TestMethodOptions.TestContext!.Context.CancellationTokenSource!.Token; + CancellationToken cancelToken = TestMethodOptions.TestContext!.Context.CancellationTokenSource.Token; if (PlatformServiceProvider.Instance.ThreadOperations.Execute(ExecuteAsyncAction, TestMethodOptions.Timeout, cancelToken)) { if (failure != null) diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestMethodRunner.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestMethodRunner.cs index 37eaec61b5..f2eb01d501 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestMethodRunner.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestMethodRunner.cs @@ -236,7 +236,7 @@ private bool ExecuteDataSourceBasedTests(List results) bool isDataDriven = false; DataSourceAttribute[] dataSourceAttribute = _testMethodInfo.GetAttributes(false); - if (dataSourceAttribute != null && dataSourceAttribute.Length == 1) + if (dataSourceAttribute is { Length: 1 }) { isDataDriven = true; Stopwatch watch = new(); @@ -294,10 +294,17 @@ private bool ExecuteDataSourceBasedTests(List results) foreach (UTF.ITestDataSource testDataSource in testDataSources) { isDataDriven = true; - IEnumerable? dataSource = null; + IEnumerable? dataSource; try { + // This code is to execute tests. To discover the tests code is in AssemblyEnumerator.ProcessTestDataSourceTests. + // Any change made here should be reflected in AssemblyEnumerator.ProcessTestDataSourceTests as well. dataSource = testDataSource.GetData(_testMethodInfo.MethodInfo); + + if (!dataSource.Any()) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, FrameworkMessages.DynamicDataIEnumerableEmpty, "GetData", testDataSource.GetType().Name)); + } } catch (Exception ex) when (ex is ArgumentException && MSTestSettings.CurrentSettings.ConsiderEmptyDataSourceAsInconclusive) { @@ -366,7 +373,7 @@ private TestResult[] ExecuteTestWithDataRow(object dataRow, int rowIndex) string displayName = string.Format(CultureInfo.CurrentCulture, Resource.DataDrivenResultDisplayName, _test.DisplayName, rowIndex); Stopwatch? stopwatch = null; - TestResult[]? testResults = null; + TestResult[]? testResults; try { stopwatch = Stopwatch.StartNew(); diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TypeCache.cs b/src/Adapter/MSTest.TestAdapter/Execution/TypeCache.cs index fd8e9b575f..b396bd7452 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TypeCache.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TypeCache.cs @@ -46,7 +46,7 @@ internal class TypeCache : MarshalByRefObject /// /// ClassInfo cache. /// - private readonly ConcurrentDictionary _classInfoCache = new(StringComparer.Ordinal); + private readonly ConcurrentDictionary _classInfoCache = new(); private readonly ConcurrentDictionary _discoverInternalsCache = new(); @@ -196,22 +196,14 @@ private static bool TryGetUnescapedManagedTypeName(TestMethod testMethod, [NotNu continue; } -#if NETCOREAPP +#if NETCOREAPP || WINDOWS_UWP if (hierarchyPart.StartsWith('\'') && hierarchyPart.EndsWith('\'')) - { - unescapedTypeNameBuilder.Append(hierarchyPart.AsSpan(1, hierarchyPart.Length - 2)); - } -#elif WINDOWS_UWP - if (hierarchyPart.StartsWith('\'') && hierarchyPart.EndsWith('\'')) - { - unescapedTypeNameBuilder.Append(hierarchyPart.Substring(1, hierarchyPart.Length - 2)); - } #else if (hierarchyPart.StartsWith("'", StringComparison.Ordinal) && hierarchyPart.EndsWith("'", StringComparison.Ordinal)) +#endif { - unescapedTypeNameBuilder.Append(hierarchyPart.Substring(1, hierarchyPart.Length - 2)); + unescapedTypeNameBuilder.Append(hierarchyPart, 1, hierarchyPart.Length - 2); } -#endif else { unescapedTypeNameBuilder.Append(hierarchyPart); @@ -277,8 +269,7 @@ private static bool TryGetUnescapedManagedTypeName(TestMethod testMethod, [NotNu /// The . private TestClassInfo CreateClassInfo(Type classType, TestMethod testMethod) { - IEnumerable constructors = classType.GetTypeInfo().DeclaredConstructors; - ConstructorInfo? constructor = constructors.FirstOrDefault(ctor => ctor.GetParameters().Length == 0 && ctor.IsPublic); + ConstructorInfo? constructor = classType.GetConstructor([]); if (constructor == null) { @@ -290,7 +281,7 @@ private TestClassInfo CreateClassInfo(Type classType, TestMethod testMethod) TestAssemblyInfo assemblyInfo = GetAssemblyInfo(classType); - TestClassAttribute? testClassAttribute = ReflectHelper.GetDerivedAttribute(classType, false); + TestClassAttribute? testClassAttribute = ReflectHelper.Instance.GetFirstDerivedAttributeOrDefault(classType, inherit: false); DebugEx.Assert(testClassAttribute is not null, "testClassAttribute is null"); var classInfo = new TestClassInfo(classType, constructor, testContextProperty, testClassAttribute, assemblyInfo); @@ -306,32 +297,31 @@ private TestClassInfo CreateClassInfo(Type classType, TestMethod testMethod) foreach (MethodInfo methodInfo in classType.GetTypeInfo().DeclaredMethods) { - // Update test initialize/cleanup method UpdateInfoIfTestInitializeOrCleanupMethod(classInfo, methodInfo, false, instanceMethods); - // Update class initialize/cleanup method UpdateInfoIfClassInitializeOrCleanupMethod(classInfo, methodInfo, false, ref initAndCleanupMethods); } - Type? baseType = classType.GetTypeInfo().BaseType; - while (baseType != null) + Type? baseType = classType.BaseType; + // PERF: Don't inspect object, no test methods or setups can be defined on it. + while (baseType != null && baseType != typeof(object)) { foreach (MethodInfo methodInfo in baseType.GetTypeInfo().DeclaredMethods) { - if (methodInfo.IsPublic && !methodInfo.IsStatic) + if (methodInfo is { IsPublic: true, IsStatic: false }) { // Update test initialize/cleanup method from base type. UpdateInfoIfTestInitializeOrCleanupMethod(classInfo, methodInfo, true, instanceMethods); } - if (methodInfo.IsPublic && methodInfo.IsStatic) + if (methodInfo is { IsPublic: true, IsStatic: true }) { UpdateInfoIfClassInitializeOrCleanupMethod(classInfo, methodInfo, true, ref initAndCleanupMethods); } } UpdateInfoWithInitializeAndCleanupMethods(classInfo, ref initAndCleanupMethods); - baseType = baseType.GetTypeInfo().BaseType; + baseType = baseType.BaseType; } return classInfo; @@ -380,7 +370,7 @@ private TestClassInfo CreateClassInfo(Type classType, TestMethod testMethod) /// The instance. private TestAssemblyInfo GetAssemblyInfo(Type type) { - Assembly assembly = type.GetTypeInfo().Assembly; + Assembly assembly = type.Assembly; if (_testAssemblyInfoCache.TryGetValue(assembly, out TestAssemblyInfo? assemblyInfo)) { @@ -402,8 +392,7 @@ private TestAssemblyInfo GetAssemblyInfo(Type type) { // Only examine classes which are TestClass or derives from TestClass attribute TypeInfo typeInfo = t.GetTypeInfo(); - if (!_reflectionHelper.IsAttributeDefined(typeInfo, inherit: true) && - !_reflectionHelper.HasAttributeDerivedFrom(typeInfo, true)) + if (!_reflectionHelper.IsDerivedAttributeDefined(typeInfo, inherit: true)) { continue; } @@ -426,16 +415,15 @@ private TestAssemblyInfo GetAssemblyInfo(Type type) { assemblyInfo.AssemblyInitializeMethod = methodInfo; - if (_reflectionHelper.IsAttributeDefined(methodInfo, false)) + TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); + if (timeoutAttribute != null) { - if (!methodInfo.HasCorrectTimeout()) + if (!methodInfo.HasCorrectTimeout(timeoutAttribute)) { string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); throw new TypeInspectionException(message); } - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetAttribute(methodInfo); - DebugEx.Assert(timeoutAttribute != null, "TimeoutAttribute cannot be null"); assemblyInfo.AssemblyInitializeMethodTimeoutMilliseconds = timeoutAttribute.Timeout; } else if (MSTestSettings.CurrentSettings.AssemblyInitializeTimeout > 0) @@ -446,16 +434,15 @@ private TestAssemblyInfo GetAssemblyInfo(Type type) else if (IsAssemblyOrClassCleanupMethod(methodInfo)) { assemblyInfo.AssemblyCleanupMethod = methodInfo; - if (_reflectionHelper.IsAttributeDefined(methodInfo, false)) + TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); + if (timeoutAttribute != null) { - if (!methodInfo.HasCorrectTimeout()) + if (!methodInfo.HasCorrectTimeout(timeoutAttribute)) { string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); throw new TypeInspectionException(message); } - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetAttribute(methodInfo); - DebugEx.Assert(timeoutAttribute != null, "TimeoutAttribute cannot be null"); assemblyInfo.AssemblyCleanupMethodTimeoutMilliseconds = timeoutAttribute.Timeout; } else if (MSTestSettings.CurrentSettings.AssemblyCleanupTimeout > 0) @@ -480,7 +467,12 @@ private TestAssemblyInfo GetAssemblyInfo(Type type) private bool IsAssemblyOrClassInitializeMethod(MethodInfo methodInfo) where TInitializeAttribute : Attribute { - if (!_reflectionHelper.IsAttributeDefined(methodInfo, false)) + // TODO: this would be inconsistent with the codebase, but potential perf gain, issue: https://github.com/microsoft/testfx/issues/2999 + // if (!methodInfo.IsStatic) + // { + // return false; + // } + if (!_reflectionHelper.IsNonDerivedAttributeDefined(methodInfo, false)) { return false; } @@ -503,7 +495,12 @@ private bool IsAssemblyOrClassInitializeMethod(MethodInfo private bool IsAssemblyOrClassCleanupMethod(MethodInfo methodInfo) where TCleanupAttribute : Attribute { - if (!_reflectionHelper.IsAttributeDefined(methodInfo, false)) + // TODO: this would be inconsistent with the codebase, but potential perf gain, issue: https://github.com/microsoft/testfx/issues/2999 + // if (!methodInfo.IsStatic) + // { + // return false; + // } + if (!_reflectionHelper.IsNonDerivedAttributeDefined(methodInfo, false)) { return false; } @@ -557,16 +554,15 @@ private void UpdateInfoIfClassInitializeOrCleanupMethod( if (isInitializeMethod) { - if (_reflectionHelper.IsAttributeDefined(methodInfo, false)) + TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); + if (timeoutAttribute != null) { - if (!methodInfo.HasCorrectTimeout()) + if (!methodInfo.HasCorrectTimeout(timeoutAttribute)) { string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); throw new TypeInspectionException(message); } - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetAttribute(methodInfo); - DebugEx.Assert(timeoutAttribute != null, "TimeoutAttribute cannot be null"); classInfo.ClassInitializeMethodTimeoutMilliseconds.Add(methodInfo, timeoutAttribute.Timeout); } else if (MSTestSettings.CurrentSettings.ClassInitializeTimeout > 0) @@ -576,7 +572,7 @@ private void UpdateInfoIfClassInitializeOrCleanupMethod( if (isBase) { - if (_reflectionHelper.GetCustomAttribute(methodInfo)! + if (_reflectionHelper.GetFirstDerivedAttributeOrDefault(methodInfo, inherit: true)? .InheritanceBehavior == InheritanceBehavior.BeforeEachDerivedClass) { initAndCleanupMethods[0] = methodInfo; @@ -591,16 +587,15 @@ private void UpdateInfoIfClassInitializeOrCleanupMethod( if (isCleanupMethod) { - if (_reflectionHelper.IsAttributeDefined(methodInfo, false)) + TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); + if (timeoutAttribute != null) { - if (!methodInfo.HasCorrectTimeout()) + if (!methodInfo.HasCorrectTimeout(timeoutAttribute)) { string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); throw new TypeInspectionException(message); } - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetAttribute(methodInfo); - DebugEx.Assert(timeoutAttribute != null, "TimeoutAttribute cannot be null"); classInfo.ClassCleanupMethodTimeoutMilliseconds.Add(methodInfo, timeoutAttribute.Timeout); } else if (MSTestSettings.CurrentSettings.ClassCleanupTimeout > 0) @@ -610,7 +605,7 @@ private void UpdateInfoIfClassInitializeOrCleanupMethod( if (isBase) { - if (_reflectionHelper.GetCustomAttribute(methodInfo)! + if (_reflectionHelper.GetFirstDerivedAttributeOrDefault(methodInfo, inherit: true)? .InheritanceBehavior == InheritanceBehavior.BeforeEachDerivedClass) { initAndCleanupMethods[1] = methodInfo; @@ -637,8 +632,8 @@ private void UpdateInfoIfTestInitializeOrCleanupMethod( bool isBase, Dictionary instanceMethods) { - bool hasTestInitialize = _reflectionHelper.IsAttributeDefined(methodInfo, inherit: false); - bool hasTestCleanup = _reflectionHelper.IsAttributeDefined(methodInfo, inherit: false); + bool hasTestInitialize = _reflectionHelper.IsNonDerivedAttributeDefined(methodInfo, inherit: false); + bool hasTestCleanup = _reflectionHelper.IsNonDerivedAttributeDefined(methodInfo, inherit: false); if (!hasTestCleanup && !hasTestInitialize) { @@ -658,16 +653,15 @@ private void UpdateInfoIfTestInitializeOrCleanupMethod( if (hasTestInitialize) { - if (_reflectionHelper.IsAttributeDefined(methodInfo, false)) + TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); + if (timeoutAttribute != null) { - if (!methodInfo.HasCorrectTimeout()) + if (!methodInfo.HasCorrectTimeout(timeoutAttribute)) { string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); throw new TypeInspectionException(message); } - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetAttribute(methodInfo); - DebugEx.Assert(timeoutAttribute != null, "TimeoutAttribute cannot be null"); classInfo.TestInitializeMethodTimeoutMilliseconds.Add(methodInfo, timeoutAttribute.Timeout); } else if (MSTestSettings.CurrentSettings.TestInitializeTimeout > 0) @@ -690,16 +684,15 @@ private void UpdateInfoIfTestInitializeOrCleanupMethod( if (hasTestCleanup) { - if (_reflectionHelper.IsAttributeDefined(methodInfo, false)) + TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); + if (timeoutAttribute != null) { - if (!methodInfo.HasCorrectTimeout()) + if (!methodInfo.HasCorrectTimeout(timeoutAttribute)) { string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); throw new TypeInspectionException(message); } - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetAttribute(methodInfo); - DebugEx.Assert(timeoutAttribute != null, "TimeoutAttribute cannot be null"); classInfo.TestCleanupMethodTimeoutMilliseconds.Add(methodInfo, timeoutAttribute.Timeout); } else if (MSTestSettings.CurrentSettings.TestCleanupTimeout > 0) @@ -742,11 +735,6 @@ private void UpdateInfoIfTestInitializeOrCleanupMethod( DebugEx.Assert(testClassInfo != null, "testClassInfo is Null"); MethodInfo methodInfo = GetMethodInfoForTestMethod(testMethod, testClassInfo); - if (methodInfo == null) - { - // Means the specified test method could not be found. - return null; - } ExpectedExceptionBaseAttribute? expectedExceptionAttribute = _reflectionHelper.ResolveExpectedExceptionHelper(methodInfo, testMethod); int timeout = GetTestTimeout(methodInfo, testMethod); @@ -775,7 +763,7 @@ private void UpdateInfoIfTestInitializeOrCleanupMethod( private TestMethodAttribute? GetTestMethodAttribute(MethodInfo methodInfo, TestClassInfo testClassInfo) { // Get the derived TestMethod attribute from reflection - TestMethodAttribute? testMethodAttribute = _reflectionHelper.GetDerivedAttribute(methodInfo, false); + TestMethodAttribute? testMethodAttribute = _reflectionHelper.GetFirstDerivedAttributeOrDefault(methodInfo, inherit: false); // Get the derived TestMethod attribute from Extended TestClass Attribute // If the extended TestClass Attribute doesn't have extended TestMethod attribute then base class returns back the original testMethod Attribute @@ -841,32 +829,25 @@ private MethodInfo GetMethodInfoForTestMethod(TestMethod testMethod, TestClassIn private static MethodInfo? GetMethodInfoUsingRuntimeMethods(TestMethod testMethod, TestClassInfo testClassInfo, bool discoverInternals) { - MethodInfo? testMethodInfo; + IEnumerable methods = testClassInfo + .ClassType + .GetRuntimeMethods() + .Where(method => method.Name == testMethod.Name && + method.HasCorrectTestMethodSignature(true, discoverInternals)); - MethodInfo[] methodsInClass = testClassInfo.ClassType.GetRuntimeMethods().ToArray(); - - if (testMethod.DeclaringClassFullName != null) - { - // Only find methods that match the given declaring name. - testMethodInfo = - Array.Find( - methodsInClass, - method => method.Name.Equals(testMethod.Name, StringComparison.Ordinal) - && method.DeclaringType!.FullName!.Equals(testMethod.DeclaringClassFullName, StringComparison.Ordinal) - && method.HasCorrectTestMethodSignature(true, discoverInternals)); - } - else + if (testMethod.DeclaringClassFullName == null) { // Either the declaring class is the same as the test class, or // the declaring class information wasn't passed in the test case. // Prioritize the former while maintaining previous behavior for the latter. string? className = testClassInfo.ClassType.FullName; - testMethodInfo = - methodsInClass.Where(method => method.Name.Equals(testMethod.Name, StringComparison.Ordinal) && method.HasCorrectTestMethodSignature(true, discoverInternals)) - .OrderByDescending(method => method.DeclaringType!.FullName!.Equals(className, StringComparison.Ordinal)).FirstOrDefault(); + return methods + .OrderByDescending(method => method.DeclaringType!.FullName == className) + .FirstOrDefault(); } - return testMethodInfo; + // Only find methods that match the given declaring name. + return methods.FirstOrDefault(method => method.DeclaringType!.FullName == testMethod.DeclaringClassFullName); } /// @@ -878,12 +859,12 @@ private MethodInfo GetMethodInfoForTestMethod(TestMethod testMethod, TestClassIn private int GetTestTimeout(MethodInfo methodInfo, TestMethod testMethod) { DebugEx.Assert(methodInfo != null, "TestMethod should be non-null"); - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetAttribute(methodInfo); + TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); int globalTimeout = MSTestSettings.CurrentSettings.TestTimeout; if (timeoutAttribute != null) { - if (!methodInfo.HasCorrectTimeout()) + if (!methodInfo.HasCorrectTimeout(timeoutAttribute)) { string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, testMethod.FullClassName, testMethod.Name); throw new TypeInspectionException(message); diff --git a/src/Adapter/MSTest.TestAdapter/Execution/UnitTestRunner.cs b/src/Adapter/MSTest.TestAdapter/Execution/UnitTestRunner.cs index 2bae363395..4f1fc1ea4e 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/UnitTestRunner.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/UnitTestRunner.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Concurrent; @@ -23,6 +23,8 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; /// internal class UnitTestRunner : MarshalByRefObject { + private readonly ConcurrentDictionary _fixtureTests = new(); + /// /// Type cache. /// @@ -97,6 +99,44 @@ internal void InitializeClassCleanupManager(IEnumerable testsTo _reflectHelper); } + internal FixtureTestResult GetFixtureTestResult(TestMethod testMethod, string fixtureType) + { + // For the fixture methods, we need to return the appropriate result. + // Get matching testMethodInfo from the cache and return UnitTestOutcome for the fixture test. + if (_fixtureTests.TryGetValue(testMethod.AssemblyName + testMethod.FullClassName, out TestMethodInfo? testMethodInfo)) + { + if (fixtureType == Constants.ClassInitializeFixtureTrait) + { + return testMethodInfo.Parent.IsClassInitializeExecuted + ? new(true, GetOutcome(testMethodInfo.Parent.ClassInitializationException), testMethodInfo.Parent.ClassInitializationException?.Message) + : new(true, ObjectModel.UnitTestOutcome.Inconclusive, null); + } + + if (fixtureType == Constants.ClassCleanupFixtureTrait) + { + return testMethodInfo.Parent.IsClassInitializeExecuted + ? new(testMethodInfo.Parent.IsClassInitializeExecuted, GetOutcome(testMethodInfo.Parent.ClassCleanupException), testMethodInfo.Parent.ClassCleanupException?.Message) + : new(true, ObjectModel.UnitTestOutcome.Inconclusive, null); + } + } + + if (_fixtureTests.TryGetValue(testMethod.AssemblyName, out testMethodInfo)) + { + if (fixtureType == Constants.AssemblyInitializeFixtureTrait) + { + return new(true, GetOutcome(testMethodInfo.Parent.Parent.AssemblyInitializationException), testMethodInfo.Parent.Parent.AssemblyInitializationException?.Message); + } + else if (fixtureType == Constants.AssemblyCleanupFixtureTrait) + { + return new(true, GetOutcome(testMethodInfo.Parent.Parent.AssemblyCleanupException), testMethodInfo.Parent.Parent.AssemblyInitializationException?.Message); + } + } + + return new(false, ObjectModel.UnitTestOutcome.Inconclusive, null); + + static ObjectModel.UnitTestOutcome GetOutcome(Exception? exception) => exception == null ? ObjectModel.UnitTestOutcome.Passed : ObjectModel.UnitTestOutcome.Failed; + } + /// /// Runs a single test. /// @@ -123,24 +163,27 @@ internal UnitTestResult[] RunSingleTest(TestMethod testMethod, IDictionary(testMethodInfo.Parent.ClassType, false); + _reflectHelper.IsNonDerivedAttributeDefined(testMethodInfo.Parent.ClassType, false); bool isIgnoreAttributeOnMethod = - _reflectHelper.IsAttributeDefined(testMethodInfo.TestMethod, false); + _reflectHelper.IsNonDerivedAttributeDefined(testMethodInfo.TestMethod, false); if (isIgnoreAttributeOnClass) { @@ -291,16 +334,17 @@ public ClassCleanupManager( IEnumerable testsToRun, ClassCleanupBehavior? lifecycleFromMsTest, ClassCleanupBehavior lifecycleFromAssembly, - ReflectHelper? reflectHelper = null) + ReflectHelper reflectHelper) { + IEnumerable runnableTests = testsToRun.Where(t => t.Traits is null || !t.Traits.Any(t => t.Name == Constants.FixturesTestTrait)); _remainingTestsByClass = - new(testsToRun.GroupBy(t => t.TestMethod.FullClassName) + new(runnableTests.GroupBy(t => t.TestMethod.FullClassName) .ToDictionary( g => g.Key, g => new HashSet(g.Select(t => t.TestMethod.UniqueName)))); _lifecycleFromMsTest = lifecycleFromMsTest; _lifecycleFromAssembly = lifecycleFromAssembly; - _reflectHelper = reflectHelper ?? new ReflectHelper(); + _reflectHelper = reflectHelper; } public void MarkTestComplete(TestMethodInfo testMethodInfo, TestMethod testMethod, out bool shouldRunEndOfClassCleanup, diff --git a/src/Adapter/MSTest.TestAdapter/Extensions/ExceptionExtensions.cs b/src/Adapter/MSTest.TestAdapter/Extensions/ExceptionExtensions.cs index 18a0b014f6..7c5c35f9b3 100644 --- a/src/Adapter/MSTest.TestAdapter/Extensions/ExceptionExtensions.cs +++ b/src/Adapter/MSTest.TestAdapter/Extensions/ExceptionExtensions.cs @@ -57,7 +57,7 @@ internal static string TryGetMessage(this Exception exception) /// /// An instance. /// StackTraceInformation for the exception. - internal static StackTraceInformation? TryGetStackTraceInformation(this Exception exception) => !StringEx.IsNullOrEmpty(exception?.StackTrace) + internal static StackTraceInformation? TryGetStackTraceInformation(this Exception exception) => !StringEx.IsNullOrEmpty(exception.StackTrace) ? ExceptionHelper.CreateStackTraceInformation(exception, false, exception.StackTrace) : null; diff --git a/src/Adapter/MSTest.TestAdapter/Extensions/MethodInfoExtensions.cs b/src/Adapter/MSTest.TestAdapter/Extensions/MethodInfoExtensions.cs index e240c1e6db..453d05bcb2 100644 --- a/src/Adapter/MSTest.TestAdapter/Extensions/MethodInfoExtensions.cs +++ b/src/Adapter/MSTest.TestAdapter/Extensions/MethodInfoExtensions.cs @@ -25,8 +25,7 @@ internal static bool HasCorrectClassOrAssemblyInitializeSignature(this MethodInf ParameterInfo[] parameters = method.GetParameters(); return - method.IsStatic && - method.IsPublic && + method is { IsStatic: true, IsPublic: true } && (parameters.Length == 1) && parameters[0].ParameterType == typeof(TestContext) && method.IsValidReturnType(); @@ -42,8 +41,7 @@ internal static bool HasCorrectClassOrAssemblyCleanupSignature(this MethodInfo m DebugEx.Assert(method != null, "method should not be null."); return - method.IsStatic && - method.IsPublic && + method is { IsStatic: true, IsPublic: true } && (method.GetParameters().Length == 0) && method.IsValidReturnType(); } @@ -58,8 +56,7 @@ internal static bool HasCorrectTestInitializeOrCleanupSignature(this MethodInfo DebugEx.Assert(method != null, "method should not be null."); return - !method.IsStatic && - method.IsPublic && + method is { IsStatic: false, IsPublic: true } && (method.GetParameters().Length == 0) && method.IsValidReturnType(); } @@ -77,9 +74,7 @@ internal static bool HasCorrectTestMethodSignature(this MethodInfo method, bool DebugEx.Assert(method != null, "method should not be null."); return - !method.IsAbstract && - !method.IsStatic && - !method.IsGenericMethod && + method is { IsAbstract: false, IsStatic: false, IsGenericMethod: false } && (method.IsPublic || (discoverInternals && method.IsAssembly)) && (method.GetParameters().Length == 0 || ignoreParameterLength) && method.IsValidReturnType(); // Match return type Task for async methods only. Else return type void. @@ -89,20 +84,16 @@ internal static bool HasCorrectTestMethodSignature(this MethodInfo method, bool /// Checks whether test method has correct Timeout attribute. /// /// The method to verify. + /// The timeout attribute when we already have it. /// True if the method has the right test timeout signature. - internal static bool HasCorrectTimeout(this MethodInfo method) + internal static bool HasCorrectTimeout(this MethodInfo method, TimeoutAttribute? timeoutAttribute = null) { DebugEx.Assert(method != null, "method should not be null."); - // There should be one and only one TimeoutAttribute. - TimeoutAttribute[] attributes = ReflectHelper.GetCustomAttributes(method, false); - if (attributes?.Length != 1) - { - return false; - } + // TODO: redesign this, probably change this to GetTimeout? so we don't have to do this weird dance? + timeoutAttribute ??= ReflectHelper.Instance.GetFirstNonDerivedAttributeOrDefault(method, inherit: false); - // Timeout cannot be less than 0. - return !(attributes[0]?.Timeout < 0); + return timeoutAttribute?.Timeout > 0; } /// @@ -125,7 +116,7 @@ internal static bool IsValidReturnType(this MethodInfo method) /// Compiler generated type name for given async test method.. internal static string? GetAsyncTypeName(this MethodInfo method) { - AsyncStateMachineAttribute? asyncStateMachineAttribute = ReflectHelper.GetCustomAttributes(method, false).FirstOrDefault(); + AsyncStateMachineAttribute? asyncStateMachineAttribute = ReflectHelper.Instance.GetFirstNonDerivedAttributeOrDefault(method, inherit: false); return asyncStateMachineAttribute?.StateMachineType?.FullName; } @@ -148,7 +139,7 @@ internal static void InvokeAsSynchronousTask(this MethodInfo methodInfo, object? // check if test method expected parameter values but no test data was provided, // throw error with appropriate message. - if (methodParameters != null && methodParameters.Length > 0 && arguments == null) + if (methodParameters is { Length: > 0 } && arguments == null) { throw new TestFailedException( ObjectModel.UnitTestOutcome.Error, @@ -159,47 +150,61 @@ internal static void InvokeAsSynchronousTask(this MethodInfo methodInfo, object? methodInfo.Name)); } - Task? task; + object? invokeResult; if (arguments is not null - && methodParameters?.Length == 1 + && methodParameters.Length == 1 && methodParameters[0].ParameterType == typeof(object[])) { - task = methodInfo.Invoke(classInstance, new[] { arguments }) as Task; + invokeResult = methodInfo.Invoke(classInstance, [arguments]); } else { int methodParametersLengthOrZero = methodParameters?.Length ?? 0; int argumentsLengthOrZero = arguments?.Length ?? 0; - if (methodParametersLengthOrZero != argumentsLengthOrZero) + +#if WINDOWS_UWP + // There is a bug with UWP in release mode where the arguments are wrapped in an object[], so we need to unwrap it. + // See https://github.com/microsoft/testfx/issues/3071 + if (argumentsLengthOrZero == 1 + && argumentsLengthOrZero < methodParametersLengthOrZero + && arguments![0] is object[] args) { - throw GetParameterCountMismatchException(methodInfo, arguments, methodParameters, methodParametersLengthOrZero, argumentsLengthOrZero, innerException: null); + arguments = args; + argumentsLengthOrZero = args.Length; } +#endif try { - task = methodInfo.Invoke(classInstance, arguments) as Task; + invokeResult = methodInfo.Invoke(classInstance, arguments); } catch (Exception ex) when (ex is TargetParameterCountException or ArgumentException) { - throw GetParameterCountMismatchException(methodInfo, arguments, methodParameters, methodParametersLengthOrZero, argumentsLengthOrZero, ex); + throw new TestFailedException( + ObjectModel.UnitTestOutcome.Error, + string.Format( + CultureInfo.InvariantCulture, + Resource.CannotRunTestArgumentsMismatchError, + methodInfo.DeclaringType!.FullName, + methodInfo.Name, + methodParametersLengthOrZero, + string.Join(", ", methodParameters?.Select(p => p.ParameterType.Name) ?? Array.Empty()), + argumentsLengthOrZero, + string.Join(", ", arguments?.Select(a => a?.GetType().Name ?? "null") ?? Array.Empty())), ex); } } // If methodInfo is an async method, wait for returned task - task?.GetAwaiter().GetResult(); + if (invokeResult is Task task) + { + task.GetAwaiter().GetResult(); + } +#if NET6_0_OR_GREATER + else if (invokeResult is ValueTask valueTask) + { + valueTask.GetAwaiter().GetResult(); + } +#endif } - - private static TestFailedException GetParameterCountMismatchException(MethodInfo methodInfo, object?[]? arguments, ParameterInfo[]? methodParameters, int methodParametersLengthOrZero, int argumentsLengthOrZero, Exception? innerException) => - new( - ObjectModel.UnitTestOutcome.Error, - string.Format( - CultureInfo.InvariantCulture, - Resource.CannotRunTestArgumentsMismatchError, - methodInfo.DeclaringType!.FullName, - methodInfo.Name, - methodParametersLengthOrZero, - string.Join(", ", methodParameters?.Select(p => p.ParameterType.Name) ?? Array.Empty()), - argumentsLengthOrZero, - string.Join(", ", arguments?.Select(a => a?.GetType().Name ?? "null") ?? Array.Empty())), innerException); } diff --git a/src/Adapter/MSTest.TestAdapter/Extensions/TestCaseExtensions.cs b/src/Adapter/MSTest.TestAdapter/Extensions/TestCaseExtensions.cs index 27ba8ce5d2..47314abae2 100644 --- a/src/Adapter/MSTest.TestAdapter/Extensions/TestCaseExtensions.cs +++ b/src/Adapter/MSTest.TestAdapter/Extensions/TestCaseExtensions.cs @@ -127,13 +127,13 @@ internal static UnitTestElement ToUnitTestElement(this TestCase testCase, string } string[]? workItemIds = testCase.GetPropertyValue(Constants.WorkItemIdsProperty, null); - if (workItemIds != null && workItemIds.Length > 0) + if (workItemIds is { Length: > 0 }) { testElement.WorkItemIds = workItemIds; } KeyValuePair[]? deploymentItems = testCase.GetPropertyValue[]>(Constants.DeploymentItemsProperty, null); - if (deploymentItems != null && deploymentItems.Length > 0) + if (deploymentItems is { Length: > 0 }) { testElement.DeploymentItems = deploymentItems; } diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/AttributeComparer.cs b/src/Adapter/MSTest.TestAdapter/Helpers/AttributeComparer.cs new file mode 100644 index 0000000000..5736b8295b --- /dev/null +++ b/src/Adapter/MSTest.TestAdapter/Helpers/AttributeComparer.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; + +internal sealed class AttributeComparer +{ + public static bool IsNonDerived(Attribute attribute) => + attribute is TAttribute; + + public static bool IsDerived(Attribute attribute) + { + Type attributeType = attribute.GetType(); + + // IsSubclassOf returns false when the types are equal. + if (attributeType == typeof(TAttribute)) + { + return true; + } + + // IsAssignableFrom also does this internally, but later falls to check generic + // and we don't need that. + if (!typeof(TAttribute).IsInterface) + { + // This returns false when TAttribute is interface (like ITestDataSource). + return attributeType.IsSubclassOf(typeof(TAttribute)); + } + + return typeof(TAttribute).IsAssignableFrom(attributeType); + } +} diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/MethodRunner.cs b/src/Adapter/MSTest.TestAdapter/Helpers/FixtureMethodRunner.cs similarity index 81% rename from src/Adapter/MSTest.TestAdapter/Helpers/MethodRunner.cs rename to src/Adapter/MSTest.TestAdapter/Helpers/FixtureMethodRunner.cs index 60476b5054..6e7ed30f5e 100644 --- a/src/Adapter/MSTest.TestAdapter/Helpers/MethodRunner.cs +++ b/src/Adapter/MSTest.TestAdapter/Helpers/FixtureMethodRunner.cs @@ -6,17 +6,20 @@ using System.Runtime.InteropServices; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using UnitTestOutcome = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome; namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; -internal static class MethodRunner +internal static class FixtureMethodRunner { internal static TestFailedException? RunWithTimeoutAndCancellation( Action action, CancellationTokenSource cancellationTokenSource, int? timeout, MethodInfo methodInfo, - string methodCancelledMessageFormat, string methodTimedOutMessageFormat) + IExecutionContextScope executionContextScope, string methodCancelledMessageFormat, string methodTimedOutMessageFormat) { + // If no timeout is specified, we can run the action directly. This avoids any overhead of creating a task/thread and + // ensures that the execution context is preserved (as we run the action on the current thread). if (timeout is null) { action(); @@ -25,16 +28,16 @@ internal static class MethodRunner // We need to start a thread to handle "cancellation" and "timeout" scenarios. return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Thread.CurrentThread.GetApartmentState() == ApartmentState.STA - ? RunWithTimeoutAndCancellationWithSTAThread(action, cancellationTokenSource, timeout, methodInfo, methodCancelledMessageFormat, methodTimedOutMessageFormat) - : RunWithTimeoutAndCancellationWithThreadPool(action, cancellationTokenSource, timeout, methodInfo, methodCancelledMessageFormat, methodTimedOutMessageFormat); + ? RunWithTimeoutAndCancellationWithSTAThread(action, cancellationTokenSource, timeout, methodInfo, executionContextScope, methodCancelledMessageFormat, methodTimedOutMessageFormat) + : RunWithTimeoutAndCancellationWithThreadPool(action, cancellationTokenSource, timeout, methodInfo, executionContextScope, methodCancelledMessageFormat, methodTimedOutMessageFormat); } private static TestFailedException? RunWithTimeoutAndCancellationWithThreadPool( Action action, CancellationTokenSource cancellationTokenSource, int? timeout, MethodInfo methodInfo, - string methodCancelledMessageFormat, string methodTimedOutMessageFormat) + IExecutionContextScope executionContextScope, string methodCancelledMessageFormat, string methodTimedOutMessageFormat) { Exception? realException = null; - Task? executionTask = null; + Task? executionTask; try { executionTask = Task.Run( @@ -42,7 +45,7 @@ internal static class MethodRunner { try { - action(); + ExecutionContextService.RunActionOnContext(action, executionContextScope); } catch (Exception ex) { @@ -82,7 +85,8 @@ internal static class MethodRunner CultureInfo.InvariantCulture, methodTimedOutMessageFormat, methodInfo.DeclaringType!.FullName, - methodInfo.Name)); + methodInfo.Name, + timeout)); } catch (Exception ex) when (ex is OperationCanceledException @@ -109,14 +113,14 @@ internal static class MethodRunner #endif private static TestFailedException? RunWithTimeoutAndCancellationWithSTAThread( Action action, CancellationTokenSource cancellationTokenSource, int? timeout, MethodInfo methodInfo, - string methodCancelledMessageFormat, string methodTimedOutMessageFormat) + IExecutionContextScope executionContextScope, string methodCancelledMessageFormat, string methodTimedOutMessageFormat) { Exception? realException = null; Thread executionThread = new(new ThreadStart(() => { try { - action(); + ExecutionContextService.RunActionOnContext(action, executionContextScope); } catch (Exception ex) { @@ -164,7 +168,8 @@ internal static class MethodRunner CultureInfo.InvariantCulture, methodTimedOutMessageFormat, methodInfo.DeclaringType!.FullName, - methodInfo.Name)); + methodInfo.Name, + timeout)); } catch (Exception ex) when diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/ReflectHelper.cs b/src/Adapter/MSTest.TestAdapter/Helpers/ReflectHelper.cs index ac1a2be06e..096b844344 100644 --- a/src/Adapter/MSTest.TestAdapter/Helpers/ReflectHelper.cs +++ b/src/Adapter/MSTest.TestAdapter/Helpers/ReflectHelper.cs @@ -1,7 +1,7 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; +using System.Collections.Concurrent; using System.Globalization; using System.Reflection; using System.Security; @@ -15,27 +15,32 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; internal class ReflectHelper : MarshalByRefObject { - private static readonly Lazy InstanceValue = new(() => new ReflectHelper()); +#pragma warning disable RS0030 // Do not use banned APIs + private static readonly Lazy InstanceValue = new(() => new()); +#pragma warning restore RS0030 // Do not use banned APIs - /// - /// Contains the memberInfo Vs the name/type of the attributes defined on that member. (FYI: - MemberInfo denotes properties, fields, methods, events). - /// - private readonly Dictionary> _attributeCache = []; + // PERF: This was moved from Dictionary> to Concurrent + // storing an array allows us to store multiple attributes of the same type if we find them. It also has lower memory footprint, and is faster + // when we are going through the whole collection. Giving us overall better perf. + private readonly ConcurrentDictionary _inheritedAttributeCache = []; + private readonly ConcurrentDictionary _nonInheritedAttributeCache = []; - internal ReflectHelper() + internal /* for tests only, because of Moq */ ReflectHelper() { } + private readonly AttributeComparer _attributeComparer = new(); + public static ReflectHelper Instance => InstanceValue.Value; /// - /// Checks to see if the parameter memberInfo contains the parameter attribute or not. + /// Checks to see if a member or type is decorated with the given attribute. The type is checked exactly. If attribute is derived (inherits from) a class, e.g. [MyTestClass] from [TestClass] it won't match if you look for [TestClass]. The inherit parameter does not impact this checking. /// - /// Attribute to search for. + /// Attribute to search for by fully qualified name. /// Member/Type to test. - /// Look through inheritance or not. - /// True if the attribute of the specified type is defined. - public virtual bool IsAttributeDefined(MemberInfo memberInfo, bool inherit) + /// Inspect inheritance chain of the member or class. E.g. if parent class has this attribute defined. + /// True if the attribute of the specified type is defined on this member or a class. + public virtual bool IsNonDerivedAttributeDefined(MemberInfo memberInfo, bool inherit) where TAttribute : Attribute { if (memberInfo == null) @@ -44,70 +49,49 @@ public virtual bool IsAttributeDefined(MemberInfo memberInfo, bool i } // Get attributes defined on the member from the cache. - Dictionary? attributes = GetAttributes(memberInfo, inherit); - string requiredAttributeQualifiedName = typeof(TAttribute).AssemblyQualifiedName!; - if (attributes == null) + Attribute[] attributes = GetCustomAttributesCached(memberInfo, inherit); + + foreach (Attribute attribute in attributes) { - // If we could not obtain all attributes from cache, just get the one we need. - TAttribute[] specificAttributes = GetCustomAttributes(memberInfo, inherit); - return specificAttributes.Any(a => string.Equals(a.GetType().AssemblyQualifiedName, requiredAttributeQualifiedName, StringComparison.Ordinal)); + if (AttributeComparer.IsNonDerived(attribute)) + { + return true; + } } - return attributes.ContainsKey(requiredAttributeQualifiedName); + return false; } /// - /// Checks to see if the parameter memberInfo contains the parameter attribute or not. + /// Checks to see if a member or type is decorated with the given attribute. The type is checked exactly. If attribute is derived (inherits from) a class, e.g. [MyTestClass] from [TestClass] it won't match if you look for [TestClass]. The inherit parameter does not impact this checking. /// - /// Attribute to search for. - /// Member/Type to test. - /// Look through inheritance or not. - /// True if the specified attribute is defined on the type. - public virtual bool IsAttributeDefined(Type type, bool inherit) + /// Attribute to search for by fully qualified name. + /// Type to test. + /// Inspect inheritance chain of the member or class. E.g. if parent class has this attribute defined. + /// True if the attribute of the specified type is defined on this member or a class. + public virtual bool IsNonDerivedAttributeDefined(Type type, bool inherit) where TAttribute : Attribute - => IsAttributeDefined((MemberInfo)type.GetTypeInfo(), inherit); + => IsNonDerivedAttributeDefined((MemberInfo)type, inherit); /// - /// Checks to see if the parameter memberInfo contains the parameter attribute or not. + /// Checks to see if a member or type is decorated with the given attribute, or an attribute that derives from it. e.g. [MyTestClass] from [TestClass] will match if you look for [TestClass]. The inherit parameter does not impact this checking. /// /// Attribute to search for. - /// Type info to test. - /// Look through inheritance or not. - /// True if the specified attribute is defined on the type. - public virtual bool IsAttributeDefined(TypeInfo typeInfo, bool inherit) + /// Type to test. + /// Inspect inheritance chain of the member or class. E.g. if parent class has this attribute defined. + /// True if the attribute of the specified type is defined on this member or a class. + public virtual bool IsDerivedAttributeDefined(Type type, bool inherit) where TAttribute : Attribute - => IsAttributeDefined((MemberInfo)typeInfo, inherit); + => IsDerivedAttributeDefined((MemberInfo)type, inherit); /// - /// Returns true when specified class/member has attribute derived from specific attribute. + /// Checks to see if a member or type is decorated with the given attribute, or an attribute that derives from it. e.g. [MyTestClass] from [TestClass] will match if you look for [TestClass]. The inherit parameter does not impact this checking. /// - /// The base attribute type. - /// The type. - /// Should look at inheritance tree. - /// An object derived from Attribute that corresponds to the instance of found attribute. - public virtual bool HasAttributeDerivedFrom(Type type, bool inherit) - where TAttribute : Attribute - => HasAttributeDerivedFrom((MemberInfo)type.GetTypeInfo(), inherit); - - /// - /// Returns true when specified class/member has attribute derived from specific attribute. - /// - /// The base attribute type. - /// The type info. - /// Should look at inheritance tree. - /// An object derived from Attribute that corresponds to the instance of found attribute. - public virtual bool HasAttributeDerivedFrom(TypeInfo typeInfo, bool inherit) - where TAttribute : Attribute - => HasAttributeDerivedFrom((MemberInfo)typeInfo, inherit); - - /// - /// Returns true when specified class/member has attribute derived from specific attribute. - /// - /// The base attribute type. - /// The member info. - /// Should look at inheritance tree. - /// An object derived from Attribute that corresponds to the instance of found attribute. - public bool HasAttributeDerivedFrom(MemberInfo memberInfo, bool inherit) + /// Attribute to search for. + /// Member to inspect for attributes. + /// Inspect inheritance chain of the member or class. E.g. if parent class has this attribute defined. + /// True if the attribute of the specified type is defined on this member or a class. + public virtual /* for testing */ bool IsDerivedAttributeDefined(MemberInfo memberInfo, bool inherit) where TAttribute : Attribute { if (memberInfo == null) @@ -116,21 +100,14 @@ public bool HasAttributeDerivedFrom(MemberInfo memberInfo, bool inhe } // Get all attributes on the member. - Dictionary? attributes = GetAttributes(memberInfo, inherit); - if (attributes == null) - { - PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"{nameof(ReflectHelper)}.{nameof(GetAttributes)}: {Resource.FailedFetchAttributeCache}"); - - return IsAttributeDefined(memberInfo, inherit); - } + Attribute[] attributes = GetCustomAttributesCached(memberInfo, inherit); // Try to find the attribute that is derived from baseAttrType. - foreach (object attribute in attributes.Values) + foreach (Attribute attribute in attributes) { - DebugEx.Assert(attribute != null, $"{nameof(ReflectHelper)}.{nameof(GetAttributes)}: internal error: wrong value in the attributes dictionary."); + DebugEx.Assert(attribute != null, $"{nameof(ReflectHelper)}.{nameof(GetCustomAttributesCached)}: internal error: wrong value in the attributes dictionary."); - Type attributeType = attribute.GetType(); - if (attributeType.GetTypeInfo().IsSubclassOf(typeof(TAttribute))) + if (AttributeComparer.IsDerived(attribute)) { return true; } @@ -153,10 +130,10 @@ public bool HasAttributeDerivedFrom(MemberInfo memberInfo, bool inhe DebugEx.Assert(methodInfo != null, "MethodInfo should be non-null"); // Get the expected exception attribute - ExpectedExceptionBaseAttribute[]? expectedExceptions; + ExpectedExceptionBaseAttribute? expectedException; try { - expectedExceptions = GetCustomAttributes(methodInfo, true); + expectedException = GetFirstDerivedAttributeOrDefault(methodInfo, inherit: true); } catch (Exception ex) { @@ -171,27 +148,7 @@ public bool HasAttributeDerivedFrom(MemberInfo memberInfo, bool inhe throw new TypeInspectionException(errorMessage); } - if (expectedExceptions == null || expectedExceptions.Length == 0) - { - return null; - } - - // Verify that there is only one attribute (multiple attributes derived from - // ExpectedExceptionBaseAttribute are not allowed on a test method) - if (expectedExceptions.Length > 1) - { - string errorMessage = string.Format( - CultureInfo.CurrentCulture, - Resource.UTA_MultipleExpectedExceptionsOnTestMethod, - testMethod.FullClassName, - testMethod.Name); - throw new TypeInspectionException(errorMessage); - } - - // Set the expected exception attribute to use for this test - ExpectedExceptionBaseAttribute expectedException = expectedExceptions[0]; - - return expectedException; + return expectedException ?? null; } /// @@ -206,89 +163,70 @@ public bool HasAttributeDerivedFrom(MemberInfo memberInfo, bool inhe #endif public override object InitializeLifetimeService() => null!; - internal static TAttribute[]? GetAttributes(MethodBase methodBase, bool inherit) - where TAttribute : Attribute - { - TAttribute[]? attributeArray = GetCustomAttributes(methodBase, inherit); - return attributeArray == null || attributeArray.Length == 0 - ? null - : attributeArray; - } - - /// - /// Match return type of method. - /// - /// The method to inspect. - /// The return type to match. - /// True if there is a match. - internal static bool MatchReturnType(MethodInfo method, Type returnType) => method == null - ? throw new ArgumentNullException(nameof(method)) - : returnType == null ? throw new ArgumentNullException(nameof(returnType)) : method.ReturnType.Equals(returnType); - /// - /// Get custom attributes on a member for both normal and reflection only load. + /// Gets first attribute that matches the type (but is not derived from it). Use this together with attribute that does not allow multiple. + /// In such case there cannot be more attributes, and this will avoid the cost of + /// checking for more than one attribute. /// - /// Type of attribute to retrieve. - /// Member for which attributes needs to be retrieved. - /// If inherited type of attribute. - /// All attributes of give type on member. - [return: NotNullIfNotNull(nameof(memberInfo))] - internal static TAttribute[]? GetCustomAttributes(MemberInfo? memberInfo, bool inherit) - where TAttribute : Attribute + /// Type of the attribute to find. + /// The type, assembly or method. + /// If we should inspect parents of this type. + /// The attribute that is found or null. + /// Throws when multiple attributes are found (the attribute must allow multiple). + public TAttribute? GetFirstNonDerivedAttributeOrDefault(ICustomAttributeProvider attributeProvider, bool inherit) + where TAttribute : Attribute { - if (memberInfo == null) + Attribute[] cachedAttributes = GetCustomAttributesCached(attributeProvider, inherit); + + foreach (Attribute cachedAttribute in cachedAttributes) { - return null; + if (AttributeComparer.IsNonDerived(cachedAttribute)) + { + return (TAttribute)cachedAttribute; + } } - object[] attributesArray = PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes( - memberInfo, - typeof(TAttribute), - inherit); - - return attributesArray!.OfType().ToArray(); // TODO: Investigate if we rely on NRE + return null; } /// - /// Get custom attributes on a member for both normal and reflection only load. + /// Gets first attribute that matches the type or is derived from it. + /// Use this together with attribute that does not allow multiple. In such case there cannot be more attributes, and this will avoid the cost of + /// checking for more than one attribute. /// - /// Member for which attributes needs to be retrieved. - /// If inherited type of attribute. - /// All attributes of give type on member. - [return: NotNullIfNotNull(nameof(memberInfo))] - internal static object[]? GetCustomAttributes(MemberInfo memberInfo, bool inherit) + /// Type of the attribute to find. + /// The type, assembly or method. + /// If we should inspect parents of this type. + /// The attribute that is found or null. + /// Throws when multiple attributes are found (the attribute must allow multiple). + public virtual /* for tests, for moq */ TAttribute? GetFirstDerivedAttributeOrDefault(ICustomAttributeProvider attributeProvider, bool inherit) + where TAttribute : Attribute { - if (memberInfo == null) + Attribute[] cachedAttributes = GetCustomAttributesCached(attributeProvider, inherit); + + foreach (Attribute cachedAttribute in cachedAttributes) { - return null; + if (AttributeComparer.IsDerived(cachedAttribute)) + { + return (TAttribute)cachedAttribute; + } } - object[] attributesArray = PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes( - memberInfo, - inherit); - - return attributesArray!.ToArray(); // TODO: Investigate if we rely on NRE + return null; } /// - /// Returns the first attribute of the specified type or null if no attribute - /// of the specified type is set on the method. + /// Match return type of method. /// - /// The type of attribute to return. - /// The method on which the attribute is defined. - /// The attribute or null if none exists. - internal TAttribute? GetAttribute(MethodInfo method) - where TAttribute : Attribute - { - if (IsAttributeDefined(method, false)) - { - TAttribute[] attributes = GetCustomAttributes(method, false); - DebugEx.Assert(attributes.Length == 1, "Should only be one attribute."); - return attributes[0]; - } - - return null; - } + /// The method to inspect. + /// The return type to match. + /// True if there is a match. + internal static bool MatchReturnType(MethodInfo method, Type returnType) => + method == null + ? throw new ArgumentNullException(nameof(method)) + : returnType == null + ? throw new ArgumentNullException(nameof(returnType)) + : method.ReturnType.Equals(returnType); /// /// Returns true when the method is declared in the assembly where the type is declared. @@ -297,7 +235,7 @@ internal static bool MatchReturnType(MethodInfo method, Type returnType) => meth /// The type declared in the assembly to check. /// True if the method is declared in the assembly where the type is declared. internal virtual bool IsMethodDeclaredInSameAssemblyAsType(MethodInfo method, Type type) - => method.DeclaringType!.GetTypeInfo().Assembly.Equals(type.GetTypeInfo().Assembly); // TODO: Investigate if we rely on NRE + => method.DeclaringType!.Assembly.Equals(type.GetTypeInfo().Assembly); // TODO: Investigate if we rely on NRE /// /// Get categories applied to the test method. @@ -305,20 +243,13 @@ internal virtual bool IsMethodDeclaredInSameAssemblyAsType(MethodInfo method, Ty /// The member to inspect. /// The reflected type that owns . /// Categories defined. - internal virtual string[] GetCategories(MemberInfo categoryAttributeProvider, Type owningType) + internal virtual /* for tests, we are mocking this */ string[] GetTestCategories(MemberInfo categoryAttributeProvider, Type owningType) { - IEnumerable categories = GetCustomAttributesRecursively(categoryAttributeProvider, owningType); - List testCategories = []; - - if (categories != null) - { - foreach (TestCategoryBaseAttribute category in categories.Cast()) - { - testCategories.AddRange(category.TestCategories); - } - } + IEnumerable? methodCategories = GetDerivedAttributes(categoryAttributeProvider, inherit: true); + IEnumerable? typeCategories = GetDerivedAttributes(owningType, inherit: true); + IEnumerable? assemblyCategories = GetDerivedAttributes(owningType.Assembly, inherit: true); - return testCategories.ToArray(); + return methodCategories.Concat(typeCategories).Concat(assemblyCategories).SelectMany(c => c.TestCategories).ToArray(); } /// @@ -328,7 +259,7 @@ internal virtual string[] GetCategories(MemberInfo categoryAttributeProvider, Ty /// The parallelization level if set. -1 otherwise. internal static ParallelizeAttribute? GetParallelizeAttribute(Assembly assembly) => PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes(assembly, typeof(ParallelizeAttribute)) - !.OfType() // TODO: Investigate if we rely on NRE + .OfType() .FirstOrDefault(); /// @@ -338,8 +269,8 @@ internal virtual string[] GetCategories(MemberInfo categoryAttributeProvider, Ty /// The type that owns . /// True if test method should not run in parallel. internal bool IsDoNotParallelizeSet(MemberInfo testMethod, Type owningType) - => GetCustomAttributes(testMethod).Length != 0 - || GetCustomAttributes(owningType.GetTypeInfo()).Length != 0; + => IsDerivedAttributeDefined(testMethod, inherit: true) + || IsDerivedAttributeDefined(owningType, inherit: true); /// /// Get the parallelization behavior for a test assembly. @@ -348,7 +279,7 @@ internal bool IsDoNotParallelizeSet(MemberInfo testMethod, Type owningType) /// True if test assembly should not run in parallel. internal static bool IsDoNotParallelizeSet(Assembly assembly) => PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes(assembly, typeof(DoNotParallelizeAttribute)) - !.Length != 0; // TODO: Investigate if we rely on NRE + .Length != 0; /// /// Gets the class cleanup lifecycle set on an assembly. @@ -357,71 +288,9 @@ internal static bool IsDoNotParallelizeSet(Assembly assembly) /// The class cleanup lifecycle attribute if set. null otherwise. internal static ClassCleanupExecutionAttribute? GetClassCleanupAttribute(Assembly assembly) => PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes(assembly, typeof(ClassCleanupExecutionAttribute)) - !.OfType() // TODO: Investigate if we rely on NRE + .OfType() .FirstOrDefault(); - /// - /// Gets custom attributes at the class and assembly for a method. - /// - /// Method Info or Member Info or a Type. - /// The type that owns . - /// The categories of the specified type on the method. - internal IEnumerable GetCustomAttributesRecursively(MemberInfo attributeProvider, Type owningType) - { - TestCategoryBaseAttribute[]? categories = GetCustomAttributes(attributeProvider); - if (categories != null) - { - categories = categories.Concat(GetCustomAttributes(owningType.GetTypeInfo())).ToArray(); - } - - if (categories != null) - { - categories = categories.Concat(GetCustomAttributeForAssembly(owningType.GetTypeInfo())).ToArray(); - } - - return categories ?? Enumerable.Empty(); - } - - /// - /// Gets the custom attributes on the assembly of a member info - /// NOTE: having it as separate virtual method, so that we can extend it for testing. - /// - /// The attribute type to find. - /// The member to inspect. - /// Custom attributes defined. - internal virtual TAttribute[] GetCustomAttributeForAssembly(MemberInfo memberInfo) - where TAttribute : Attribute - => PlatformServiceProvider.Instance.ReflectionOperations - .GetCustomAttributes(memberInfo.Module.Assembly, typeof(TAttribute)) - !.OfType() - .ToArray(); - - /// - /// Gets the custom attributes of the provided type on a memberInfo. - /// - /// The attribute type. - /// The member to reflect on. - /// Attributes defined. - internal virtual TAttribute[] GetCustomAttributes(MemberInfo attributeProvider) - where TAttribute : Attribute - => GetCustomAttributes(attributeProvider, true); - - /// - /// Gets the first custom attribute of the provided type on a memberInfo. - /// - /// The attribute type. - /// The member to reflect on. - /// Attribute defined. - internal virtual TAttribute? GetCustomAttribute(MemberInfo attributeProvider) - where TAttribute : Attribute - { - TAttribute[] attribute = GetCustomAttributes(attributeProvider, true); - - return attribute == null || attribute.Length != 1 - ? null - : attribute[0]; - } - /// /// KeyValue pairs that are provided by TestOwnerAttribute of the given test method. /// @@ -451,14 +320,8 @@ internal virtual TAttribute[] GetCustomAttributes(MemberInfo attribu /// /// The member to inspect. /// Priority value if defined. Null otherwise. - internal virtual int? GetPriority(MemberInfo priorityAttributeProvider) - { - PriorityAttribute[] priorityAttribute = GetCustomAttributes(priorityAttributeProvider, true); - - return priorityAttribute == null || priorityAttribute.Length != 1 - ? null - : priorityAttribute[0].Priority; - } + internal virtual int? GetPriority(MemberInfo priorityAttributeProvider) => + GetFirstDerivedAttributeOrDefault(priorityAttributeProvider, inherit: true)?.Priority; /// /// Priority if any set for test method. Will return priority if attribute is applied to TestMethod @@ -466,14 +329,8 @@ internal virtual TAttribute[] GetCustomAttributes(MemberInfo attribu /// /// The member to inspect. /// Priority value if defined. Null otherwise. - internal virtual string? GetIgnoreMessage(MemberInfo ignoreAttributeProvider) - { - IgnoreAttribute[]? ignoreAttribute = GetCustomAttributes(ignoreAttributeProvider, true); - - return ignoreAttribute is null || ignoreAttribute.Length == 0 - ? null - : ignoreAttribute[0].IgnoreMessage; - } + internal virtual string? GetIgnoreMessage(MemberInfo ignoreAttributeProvider) => + GetFirstDerivedAttributeOrDefault(ignoreAttributeProvider, inherit: true)?.IgnoreMessage; /// /// Gets the class cleanup lifecycle for the class, if set. @@ -482,6 +339,7 @@ internal virtual TAttribute[] GetCustomAttributes(MemberInfo attribu /// Returns if provided, otherwise null. internal virtual ClassCleanupBehavior? GetClassCleanupBehavior(TestClassInfo classInfo) { + // TODO: not discovery related but seems expensive and unnecessary, because we do inheritance lookup, and to put the method into the stack we've already did this lookup before? if (!classInfo.HasExecutableCleanupMethod) { return null; @@ -490,9 +348,9 @@ internal virtual TAttribute[] GetCustomAttributes(MemberInfo attribu var cleanupBehaviors = new HashSet( classInfo.BaseClassCleanupMethodsStack - .Select(x => x.GetCustomAttribute(true)?.CleanupBehavior)) + .Select(x => GetFirstDerivedAttributeOrDefault(x, inherit: true)?.CleanupBehavior)) { - classInfo.ClassCleanupMethod?.GetCustomAttribute(true)?.CleanupBehavior, + classInfo.ClassCleanupMethod == null ? null : GetFirstDerivedAttributeOrDefault(classInfo.ClassCleanupMethod, inherit: true)?.CleanupBehavior, }; return cleanupBehaviors.Contains(ClassCleanupBehavior.EndOfClass) @@ -507,13 +365,11 @@ internal virtual TAttribute[] GetCustomAttributes(MemberInfo attribu /// List of traits. internal virtual IEnumerable GetTestPropertiesAsTraits(MemberInfo testPropertyProvider) { - TestPropertyAttribute[] testPropertyAttributes = GetCustomAttributes(testPropertyProvider, true); + IEnumerable testPropertyAttributes = GetDerivedAttributes(testPropertyProvider, inherit: true); foreach (TestPropertyAttribute testProperty in testPropertyAttributes) { - Trait testPropertyPair = testProperty.Name == null - ? new Trait(string.Empty, testProperty.Value) - : new Trait(testProperty.Name, testProperty.Value); + var testPropertyPair = new Trait(testProperty.Name, testProperty.Value); yield return testPropertyPair; } } @@ -522,62 +378,24 @@ internal virtual IEnumerable GetTestPropertiesAsTraits(MemberInfo testPro /// Get attribute defined on a method which is of given type of subtype of given type. /// /// The attribute type. - /// The member to inspect. - /// Look at inheritance chain. - /// An instance of the attribute. - internal TAttributeType? GetDerivedAttribute(MemberInfo memberInfo, bool inherit) - where TAttributeType : Attribute - { - Dictionary? attributes = GetAttributes(memberInfo, inherit); - if (attributes == null) - { - return null; - } - - // Try to find the attribute that is derived from baseAttrType. - foreach (object attribute in attributes.Values) - { - DebugEx.Assert(attribute != null, "ReflectHelper.DefinesAttributeDerivedFrom: internal error: wrong value in the attributes dictionary."); - - Type attributeType = attribute.GetType(); - if (attributeType.Equals(typeof(TAttributeType)) || attributeType.GetTypeInfo().IsSubclassOf(typeof(TAttributeType))) - { - return attribute as TAttributeType; - } - } - - return null; - } - - /// - /// Get attribute defined on a method which is of given type of subtype of given type. - /// - /// The attribute type. - /// The type to inspect. + /// The member to inspect. /// Look at inheritance chain. /// An instance of the attribute. - internal static TAttributeType? GetDerivedAttribute(Type type, bool inherit) + internal virtual /* for tests, for moq */ IEnumerable GetDerivedAttributes(ICustomAttributeProvider attributeProvider, bool inherit) where TAttributeType : Attribute { - object[] attributes = GetCustomAttributes(type.GetTypeInfo(), inherit); - if (attributes == null) - { - return null; - } + Attribute[] attributes = GetCustomAttributesCached(attributeProvider, inherit); // Try to find the attribute that is derived from baseAttrType. - foreach (object attribute in attributes) + foreach (Attribute attribute in attributes) { DebugEx.Assert(attribute != null, "ReflectHelper.DefinesAttributeDerivedFrom: internal error: wrong value in the attributes dictionary."); - Type attributeType = attribute.GetType(); - if (attributeType.Equals(typeof(TAttributeType)) || attributeType.GetTypeInfo().IsSubclassOf(typeof(TAttributeType))) + if (AttributeComparer.IsDerived(attribute)) { - return attribute as TAttributeType; + yield return (TAttributeType)attribute; } } - - return null; } /// @@ -585,39 +403,43 @@ internal virtual IEnumerable GetTestPropertiesAsTraits(MemberInfo testPro /// /// The member to inspect. /// owner if attribute is applied to TestMethod, else null. - private static string? GetOwner(MemberInfo ownerAttributeProvider) + private string? GetOwner(MemberInfo ownerAttributeProvider) { - OwnerAttribute[] ownerAttribute = GetCustomAttributes(ownerAttributeProvider, true); + OwnerAttribute? ownerAttribute = GetFirstDerivedAttributeOrDefault(ownerAttributeProvider, inherit: true); - return ownerAttribute == null || ownerAttribute.Length != 1 - ? null - : ownerAttribute[0].Owner; + return ownerAttribute?.Owner; } /// - /// Get the Attributes (TypeName/TypeObject) for a given member. + /// Gets and caches the attributes for the given type, or method. /// - /// The member to inspect. + /// The member to inspect. /// Look at inheritance chain. /// attributes defined. - private Dictionary? GetAttributes(MemberInfo memberInfo, bool inherit) + private Attribute[] GetCustomAttributesCached(ICustomAttributeProvider attributeProvider, bool inherit) { // If the information is cached, then use it otherwise populate the cache using // the reflection APIs. - lock (_attributeCache) - { - if (_attributeCache.TryGetValue(memberInfo, out Dictionary? attributes)) - { - return attributes; - } + return inherit + ? _inheritedAttributeCache.GetOrAdd(attributeProvider, GetAttributesInheriting) + : _nonInheritedAttributeCache.GetOrAdd(attributeProvider, GetAttributesNonInheriting); - // Populate the cache - attributes = []; + // We are avoiding func allocation here. + static Attribute[] GetAttributesInheriting(ICustomAttributeProvider key) + => GetAttributes(key, inherit: true); - object[]? customAttributesArray = null; + static Attribute[] GetAttributesNonInheriting(ICustomAttributeProvider key) + => GetAttributes(key, inherit: false); + + static Attribute[] GetAttributes(ICustomAttributeProvider attributeProvider, bool inherit) + { + // Populate the cache try { - customAttributesArray = GetCustomAttributes(memberInfo, inherit); + object[]? attributes = NotCachedReflectionAccessor.GetCustomAttributesNotCached(attributeProvider, inherit); + return attributes is Attribute[] arr + ? arr + : attributes?.Cast().ToArray() ?? Array.Empty(); } catch (Exception ex) { @@ -633,25 +455,52 @@ internal virtual IEnumerable GetTestPropertiesAsTraits(MemberInfo testPro description = string.Format(CultureInfo.CurrentCulture, Resource.ExceptionOccuredWhileGettingTheExceptionDescription, ex.GetType().FullName, ex2.GetType().FullName); // ex.GetType().FullName + } - PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(Resource.FailedToGetCustomAttribute, memberInfo.GetType().FullName!, description); + PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(Resource.FailedToGetCustomAttribute, attributeProvider.GetType().FullName!, description); - // Since we cannot check by attribute names, do it in reflection way. - // Note 1: this will not work for different version of assembly but it is better than nothing. - // Note 2: we cannot cache this because we don't know if there are other attributes defined. - return null; + return []; } + } + } - DebugEx.Assert(customAttributesArray != null, "attributes should not be null."); + internal IEnumerable? GetNonDerivedAttributes(MethodInfo methodInfo, bool inherit) + where TAttribute : Attribute + { + Attribute[] cachedAttributes = GetCustomAttributesCached(methodInfo, inherit); - foreach (object customAttribute in customAttributesArray) + foreach (Attribute cachedAttribute in cachedAttributes) + { + if (AttributeComparer.IsNonDerived(cachedAttribute)) { - Type attributeType = customAttribute.GetType(); - attributes[attributeType.AssemblyQualifiedName!] = customAttribute; + yield return (TAttribute)cachedAttribute; } + } + } - _attributeCache.Add(memberInfo, attributes); + /// + /// Reflection helper that is accessing Reflection directly, and won't cache the results. + /// + internal static class NotCachedReflectionAccessor + { + /// + /// Get custom attributes on a member without cache. Be CAREFUL where you use this, repeatedly accessing reflection without caching the results degrades the performance. + /// + /// Member for which attributes needs to be retrieved. + /// If inherited type of attribute. + /// All attributes of give type on member. + public static object[]? GetCustomAttributesNotCached(ICustomAttributeProvider attributeProvider, bool inherit) + { + object[] attributesArray = attributeProvider is MemberInfo memberInfo + ? PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes(memberInfo, inherit) + : PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes((Assembly)attributeProvider, typeof(Attribute)); - return attributes; + return attributesArray; // TODO: Investigate if we rely on NRE } } + + internal /* for tests */ void ClearCache() + { + // Tests manipulate the platform reflection provider, and we end up caching different attributes than the class / method actually has. + _inheritedAttributeCache.Clear(); + _nonInheritedAttributeCache.Clear(); + } } diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/RunSettingsUtilities.cs b/src/Adapter/MSTest.TestAdapter/Helpers/RunSettingsUtilities.cs index 76effa9bd6..5f7e649824 100644 --- a/src/Adapter/MSTest.TestAdapter/Helpers/RunSettingsUtilities.cs +++ b/src/Adapter/MSTest.TestAdapter/Helpers/RunSettingsUtilities.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Xml; @@ -34,17 +35,8 @@ internal static XmlReaderSettings ReaderSettings /// The runsettings xml. /// The test run parameters. /// If there is no test run parameters section defined in the settingsxml a blank dictionary is returned. - internal static Dictionary GetTestRunParameters(string? settingsXml) - { - Dictionary? nodeValue = GetNodeValue(settingsXml, Constants.TestRunParametersName, TestRunParameters.FromXml); - if (nodeValue == default(Dictionary)) - { - // Return default. - nodeValue = []; - } - - return nodeValue; - } + internal static Dictionary? GetTestRunParameters([StringSyntax(StringSyntaxAttribute.Xml, nameof(settingsXml))] string? settingsXml) + => GetNodeValue(settingsXml, Constants.TestRunParametersName, TestRunParameters.FromXml); /// /// Throws if the node has an attribute. @@ -65,7 +57,10 @@ internal static void ThrowOnHasAttributes(XmlReader reader) } } - private static T? GetNodeValue(string? settingsXml, string nodeName, Func nodeParser) + private static T? GetNodeValue( + [StringSyntax(StringSyntaxAttribute.Xml, nameof(settingsXml))] string? settingsXml, + string nodeName, + Func nodeParser) { if (StringEx.IsNullOrWhiteSpace(settingsXml)) { @@ -73,26 +68,24 @@ internal static void ThrowOnHasAttributes(XmlReader reader) } // use XmlReader to avoid loading of the plugins in client code (mainly from VS). - using (StringReader stringReader = new(settingsXml)) - { - var reader = XmlReader.Create(stringReader, ReaderSettings); + using StringReader stringReader = new(settingsXml); + var reader = XmlReader.Create(stringReader, ReaderSettings); - // read to the fist child - XmlReaderUtilities.ReadToRootNode(reader); - reader.ReadToNextElement(); + // read to the fist child + XmlReaderUtilities.ReadToRootNode(reader); + reader.ReadToNextElement(); - // Read till we reach nodeName element or reach EOF - while (!string.Equals(reader.Name, nodeName, StringComparison.OrdinalIgnoreCase) - && !reader.EOF) - { - reader.SkipToNextElement(); - } + // Read till we reach nodeName element or reach EOF + while (!string.Equals(reader.Name, nodeName, StringComparison.OrdinalIgnoreCase) + && !reader.EOF) + { + reader.SkipToNextElement(); + } - if (!reader.EOF) - { - // read nodeName element. - return nodeParser(reader); - } + if (!reader.EOF) + { + // read nodeName element. + return nodeParser(reader); } return default; diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/RuntimeContext.cs b/src/Adapter/MSTest.TestAdapter/Helpers/RuntimeContext.cs new file mode 100644 index 0000000000..ddb0a272bf --- /dev/null +++ b/src/Adapter/MSTest.TestAdapter/Helpers/RuntimeContext.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; + +internal static class RuntimeContext +{ + private static readonly Lazy IsHotReloadEnabledLazy = new(() => + // TODO: We should be using a capability from the runner instead of looking at environment variables. + Environment.GetEnvironmentVariable("DOTNET_WATCH") == "1" + || Environment.GetEnvironmentVariable("TESTINGPLATFORM_HOTRELOAD_ENABLED") == "1"); + + public static bool IsHotReloadEnabled => IsHotReloadEnabledLazy.Value; +} diff --git a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj index 8c03e9fb72..ca460a3965 100644 --- a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj +++ b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj @@ -1,11 +1,11 @@ - + - $(UwpMinimum);$(WinUiMinimum);netstandard2.0;$(NetFrameworkMinimum);$(SupportedNetFrameworks) + netstandard2.0;$(NetFrameworkMinimum);$(SupportedNetFrameworks);$(UwpMinimum);$(WinUiMinimum) $(SupportedNetFrameworks);netstandard2.0 enable true @@ -44,16 +44,17 @@ + - + @@ -75,7 +76,7 @@ - + @@ -96,6 +97,9 @@ + + + diff --git a/src/Adapter/MSTest.TestAdapter/MSTestSettings.cs b/src/Adapter/MSTest.TestAdapter/MSTestSettings.cs index cb4047cc05..ca50915dba 100644 --- a/src/Adapter/MSTest.TestAdapter/MSTestSettings.cs +++ b/src/Adapter/MSTest.TestAdapter/MSTestSettings.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Diagnostics.CodeAnalysis; @@ -191,6 +191,13 @@ public static RunConfigurationSettings RunConfigurationSettings /// public bool TreatClassAndAssemblyCleanupWarningsAsErrors { get; private set; } + /// + /// Gets a value indicating whether AssemblyInitialize, AssemblyCleanup, ClassInitialize and ClassCleanup methods are + /// reported as special tests (cannot be executed). When this feature is enabled, these methods will be reported as + /// separate entries in the TRX reports, in Test Explorer or in CLI. + /// + internal bool ConsiderFixturesAsSpecialTests { get; private set; } + /// /// Populate settings based on existing settings object. /// @@ -222,6 +229,7 @@ public static void PopulateSettings(MSTestSettings settings) CurrentSettings.ClassCleanupTimeout = settings.ClassCleanupTimeout; CurrentSettings.TestInitializeTimeout = settings.TestInitializeTimeout; CurrentSettings.TestCleanupTimeout = settings.TestCleanupTimeout; + CurrentSettings.ConsiderFixturesAsSpecialTests = settings.ConsiderFixturesAsSpecialTests; } /// @@ -234,7 +242,7 @@ public static void PopulateSettings(IDiscoveryContext? context) { RunConfigurationSettings = RunConfigurationSettings.PopulateSettings(context); - if (context == null || context.RunSettings == null || StringEx.IsNullOrEmpty(context.RunSettings.SettingsXml)) + if (context?.RunSettings == null || StringEx.IsNullOrEmpty(context.RunSettings.SettingsXml)) { // This will contain default adapter settings CurrentSettings = new MSTestSettings(); @@ -280,7 +288,9 @@ public static bool IsLegacyScenario(IMessageLogger logger) /// The xml with the settings passed from the test platform. /// The name of the adapter settings to fetch - Its either MSTest or MSTestV2. /// The settings if found. Null otherwise. - internal static MSTestSettings? GetSettings(string? runSettingsXml, string settingName) + internal static MSTestSettings? GetSettings( + [StringSyntax(StringSyntaxAttribute.Xml, nameof(runSettingsXml))] string? runSettingsXml, + string settingName) { if (StringEx.IsNullOrWhiteSpace(runSettingsXml)) { @@ -551,6 +561,16 @@ private static MSTestSettings ToSettings(XmlReader reader) break; } + case "CONSIDERFIXTURESASSPECIALTESTS": + { + if (bool.TryParse(reader.ReadInnerXml(), out result)) + { + settings.ConsiderFixturesAsSpecialTests = result; + } + + break; + } + default: { PlatformServiceProvider.Instance.SettingsProvider.Load(reader.ReadSubtree()); @@ -628,23 +648,19 @@ private static void SetParallelSettings(XmlReader reader, MSTestSettings setting } // If any of these properties are not set, resort to the defaults. - if (!settings.ParallelizationWorkers.HasValue) - { - settings.ParallelizationWorkers = Environment.ProcessorCount; - } + settings.ParallelizationWorkers ??= Environment.ProcessorCount; - if (!settings.ParallelizationScope.HasValue) - { - settings.ParallelizationScope = ExecutionScope.ClassLevel; - } + settings.ParallelizationScope ??= ExecutionScope.ClassLevel; } private static bool TryParseEnum(string value, out T result) where T : struct, Enum => Enum.TryParse(value, true, out result) && Enum.IsDefined(typeof(T), result); - private static void SetGlobalSettings(string runsettingsXml, MSTestSettings settings) + private static void SetGlobalSettings( + [StringSyntax(StringSyntaxAttribute.Xml, nameof(runsettingsXml))] string runsettingsXml, + MSTestSettings settings) { - XElement? runConfigElement = XDocument.Parse(runsettingsXml)?.Element("RunSettings")?.Element("RunConfiguration"); + XElement? runConfigElement = XDocument.Parse(runsettingsXml).Element("RunSettings")?.Element("RunConfiguration"); if (runConfigElement == null) { diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/FixtureTestResult.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/FixtureTestResult.cs new file mode 100644 index 0000000000..ec5afc1683 --- /dev/null +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/FixtureTestResult.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; + +[Serializable] +internal sealed class FixtureTestResult +{ + internal FixtureTestResult(bool isExecuted, UnitTestOutcome outcome, string? exceptionMessage) + { + IsExecuted = isExecuted; + Outcome = outcome; + ExceptionMessage = exceptionMessage; + } + + /// + /// Gets a value indicating whether the test is executed or not. + /// + public bool IsExecuted { get; } + + /// + /// Gets the outcome of the test. + /// + public UnitTestOutcome Outcome { get; } + + /// + /// Gets the exception message if any. + /// + public string? ExceptionMessage { get; } +} diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethod.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethod.cs index 81cd13d734..26c40122df 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethod.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethod.cs @@ -173,8 +173,8 @@ public string? DeclaringClassFullName internal string UniqueName => HasManagedMethodAndTypeProperties - ? $"{ManagedTypeName}.{ManagedMethodName}->{string.Join(", ", SerializedData ?? Array.Empty())}" - : $"{FullClassName}.{Name}->{string.Join(", ", SerializedData ?? Array.Empty())}"; + ? $"{ManagedTypeName}.{ManagedMethodName}->{string.Join(", ", SerializedData ?? [])}" + : $"{FullClassName}.{Name}->{string.Join(", ", SerializedData ?? [])}"; internal TestMethod Clone() => (TestMethod)MemberwiseClone(); } diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestElement.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestElement.cs index df59951b21..d5c6a223fb 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestElement.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestElement.cs @@ -144,7 +144,7 @@ internal TestCase ToTestCase() } IReadOnlyCollection hierarchy = TestMethod.Hierarchy; - if (hierarchy != null && hierarchy.Count > 0) + if (hierarchy is { Count: > 0 }) { testCase.SetHierarchy(hierarchy.ToArray()); } @@ -162,7 +162,7 @@ internal TestCase ToTestCase() } // Set only if some test category is present - if (TestCategory != null && TestCategory.Length > 0) + if (TestCategory is { Length: > 0 }) { testCase.SetPropertyValue(Constants.TestCategoryProperty, TestCategory); } @@ -199,7 +199,7 @@ internal TestCase ToTestCase() } // The list of items to deploy before running this test. - if (DeploymentItems != null && DeploymentItems.Length > 0) + if (DeploymentItems is { Length: > 0 }) { testCase.SetPropertyValue(Constants.DeploymentItemsProperty, DeploymentItems); } diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestResult.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestResult.cs index c3a7ffcafd..93b3595f00 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestResult.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestResult.cs @@ -196,7 +196,7 @@ internal TestResult ToTestResult(TestCase testCase, DateTimeOffset startTime, Da testResult.Messages.Add(testContextMessage); } - if (ResultFiles != null && ResultFiles.Count > 0) + if (ResultFiles is { Count: > 0 }) { AttachmentSet attachmentSet = new(Constants.ExecutorUri, Resource.AttachmentSetDisplayName); foreach (string resultFile in ResultFiles) diff --git a/src/Adapter/MSTest.TestAdapter/Properties/TestExtensionTypesAttribute.cs b/src/Adapter/MSTest.TestAdapter/Properties/TestExtensionTypesAttribute.cs index bce7b6890c..617a0dc406 100644 --- a/src/Adapter/MSTest.TestAdapter/Properties/TestExtensionTypesAttribute.cs +++ b/src/Adapter/MSTest.TestAdapter/Properties/TestExtensionTypesAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestPlatform; /// /// Custom Attribute to specify the exact types which should be loaded from assembly. /// -[AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Assembly)] internal sealed class TestExtensionTypesAttribute : Attribute { public TestExtensionTypesAttribute(params Type[] types) diff --git a/src/Adapter/MSTest.TestAdapter/Resources/Resource.Designer.cs b/src/Adapter/MSTest.TestAdapter/Resources/Resource.Designer.cs index 76dae04a2b..04ed583d34 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/Resource.Designer.cs +++ b/src/Adapter/MSTest.TestAdapter/Resources/Resource.Designer.cs @@ -62,7 +62,7 @@ internal Resource() { } /// - /// Looks up a localized string similar to Assembly cleanup method '{0}.{1}' timed out. + /// Looks up a localized string similar to Assembly cleanup method '{0}.{1}' timed out after {2}ms. /// internal static string AssemblyCleanupTimedOut { get { @@ -80,7 +80,7 @@ internal static string AssemblyCleanupWasCancelled { } /// - /// Looks up a localized string similar to Assembly initialize method '{0}.{1}' timed out. + /// Looks up a localized string similar to Assembly initialize method '{0}.{1}' timed out after {2}ms. /// internal static string AssemblyInitializeTimedOut { get { @@ -163,7 +163,7 @@ internal static string CannotRunTestMethodNoDataError { } /// - /// Looks up a localized string similar to Class cleanup method '{0}.{1}' timed out. + /// Looks up a localized string similar to Class cleanup method '{0}.{1}' timed out after {2}ms. /// internal static string ClassCleanupTimedOut { get { @@ -181,7 +181,7 @@ internal static string ClassCleanupWasCancelled { } /// - /// Looks up a localized string similar to Class initialize method '{0}.{1}' timed out. + /// Looks up a localized string similar to Class initialize method '{0}.{1}' timed out after {2}ms. /// internal static string ClassInitializeTimedOut { get { @@ -406,7 +406,7 @@ internal static string TestAssembly_FileDoesNotExist { } /// - /// Looks up a localized string similar to Test cleanup method '{0}.{1}' timed out. + /// Looks up a localized string similar to Test cleanup method '{0}.{1}' timed out after {2}ms. /// internal static string TestCleanupTimedOut { get { @@ -442,7 +442,7 @@ internal static string TestContextMessageBanner { } /// - /// Looks up a localized string similar to Test initialize method '{0}.{1}' timed out. + /// Looks up a localized string similar to Test initialize method '{0}.{1}' timed out after {2}ms. /// internal static string TestInitializeTimedOut { get { diff --git a/src/Adapter/MSTest.TestAdapter/Resources/Resource.resx b/src/Adapter/MSTest.TestAdapter/Resources/Resource.resx index 189af2b636..124c9530a4 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/Resource.resx +++ b/src/Adapter/MSTest.TestAdapter/Resources/Resource.resx @@ -361,13 +361,13 @@ Error: {1} {1}: Test name - Assembly initialize method '{0}.{1}' timed out + Assembly initialize method '{0}.{1}' timed out after {2}ms Assembly initialize method '{0}.{1}' was cancelled - Class initialize method '{0}.{1}' timed out + Class initialize method '{0}.{1}' timed out after {2}ms Class initialize method '{0}.{1}' was cancelled @@ -376,25 +376,25 @@ Error: {1} TestClass attribute defined on generic non-abstract class {0} - Assembly cleanup method '{0}.{1}' timed out + Assembly cleanup method '{0}.{1}' timed out after {2}ms Assembly cleanup method '{0}.{1}' was cancelled - Class cleanup method '{0}.{1}' timed out + Class cleanup method '{0}.{1}' timed out after {2}ms Class cleanup method '{0}.{1}' was cancelled - Test cleanup method '{0}.{1}' timed out + Test cleanup method '{0}.{1}' timed out after {2}ms Test cleanup method '{0}.{1}' was cancelled - Test initialize method '{0}.{1}' timed out + Test initialize method '{0}.{1}' timed out after {2}ms Test initialize method '{0}.{1}' was cancelled diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.cs.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.cs.xlf index 3751450204..19d5657bc2 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.cs.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.cs.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - Metoda čištění sestavení {0}. Vypršel časový limit {1} + Assembly cleanup method '{0}.{1}' timed out after {2}ms + Po {2} ms vypršel časový limit metody čištění sestavení {0}.{1}. @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - Vypršel časový limit metody inicializace sestavení {0}.{1}. + Assembly initialize method '{0}.{1}' timed out after {2}ms + Po {2} ms vypršel časový limit metody inicializace sestavení {0}.{1}. @@ -37,8 +37,8 @@ byl však přijat tento počet argumentů: {4} s typy {5}. - Class cleanup method '{0}.{1}' timed out - Metoda čištění třídy {0}. Vypršel časový limit {1} + Class cleanup method '{0}.{1}' timed out after {2}ms + Po {2} ms vypršel časový limit metody čištění třídy {0}.{1}. @@ -47,8 +47,8 @@ byl však přijat tento počet argumentů: {4} s typy {5}. - Class initialize method '{0}.{1}' timed out - Vypršel časový limit metody inicializace třídy {0}.{1}. + Class initialize method '{0}.{1}' timed out after {2}ms + Po {2} ms vypršel časový limit metody inicializace třídy {0}.{1}. @@ -72,8 +72,8 @@ byl však přijat tento počet argumentů: {4} s typy {5}. - Test cleanup method '{0}.{1}' timed out - Metoda čištění testu {0}. Vypršel časový limit {1} + Test cleanup method '{0}.{1}' timed out after {2}ms + Po {2} ms vypršel časový limit metody čištění testu {0}.{1}. @@ -82,8 +82,8 @@ byl však přijat tento počet argumentů: {4} s typy {5}. - Test initialize method '{0}.{1}' timed out - Metoda inicializace testu {0}. Vypršel časový limit {1} + Test initialize method '{0}.{1}' timed out after {2}ms + Po {2} ms vypršel časový limit metody inicializace testu {0}.{1}. diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.de.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.de.xlf index 16e9d614c1..813b6b8438 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.de.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.de.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - Timeout der Assemblybereinigungsmethode „{0}.{1}“ + Assembly cleanup method '{0}.{1}' timed out after {2}ms + Timeout der Assemblybereinigungsmethode "{0}.{1}" nach {2} ms @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - Timeout der Assemblyinitialisierungsmethode "{0}.{1}" + Assembly initialize method '{0}.{1}' timed out after {2}ms + Timeout der Assemblyinitialisierungsmethode "{0}.{1}" nach {2} ms @@ -37,8 +37,8 @@ aber empfing {4} Argument(e) mit den Typen „{5}“. - Class cleanup method '{0}.{1}' timed out - Timeout der Klassenbereinigungsmethode „{0}.{1}“ + Class cleanup method '{0}.{1}' timed out after {2}ms + Timeout der Klassenbereinigungsmethode "{0}.{1}" nach {2} ms @@ -47,8 +47,8 @@ aber empfing {4} Argument(e) mit den Typen „{5}“. - Class initialize method '{0}.{1}' timed out - Timeout der Klasseninitialisierungsmethode "{0}.{1}" + Class initialize method '{0}.{1}' timed out after {2}ms + Timeout der Klasseninitialisierungsmethode "{0}.{1}" nach {2} ms @@ -72,8 +72,8 @@ aber empfing {4} Argument(e) mit den Typen „{5}“. - Test cleanup method '{0}.{1}' timed out - Timeout bei Testbereinigungsmethode „{0}.{1}“ + Test cleanup method '{0}.{1}' timed out after {2}ms + Timeout der Testbereinigungsmethode "{0}.{1}" nach {2} ms @@ -82,8 +82,8 @@ aber empfing {4} Argument(e) mit den Typen „{5}“. - Test initialize method '{0}.{1}' timed out - Timeout der Testinitialisierungsmethode „{0}.{1}“ + Test initialize method '{0}.{1}' timed out after {2}ms + Timeout der Testinitialisierungsmethode "{0}.{1}" nach {2} ms diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.es.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.es.xlf index 62c60c01fb..aaf2267cd9 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.es.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.es.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - Se agotó el tiempo de espera del método de limpieza de ensamblados "{0}.{1}" + Assembly cleanup method '{0}.{1}' timed out after {2}ms + Método de limpieza de ensamblado '{0}.{1}' se agotó el tiempo de espera después de {2}ms @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - Se agotó el tiempo de espera del método de inicialización de ensamblado "{0}.{1}" + Assembly initialize method '{0}.{1}' timed out after {2}ms + Método de inicialización de ensamblado '{0}.{1}' se agotó el tiempo de espera de después de {2}ms @@ -37,8 +37,8 @@ pero recibió {4} argumento(s), con los tipos "{5}". - Class cleanup method '{0}.{1}' timed out - Se agotó el tiempo de espera del método de limpieza de clases "{0}.{1}" + Class cleanup method '{0}.{1}' timed out after {2}ms + Método de limpieza de clases '{0}.{1}' se agotó el tiempo de espera después de {2}ms @@ -47,8 +47,8 @@ pero recibió {4} argumento(s), con los tipos "{5}". - Class initialize method '{0}.{1}' timed out - Se agotó el tiempo de espera del método de inicialización de clase "{0}.{1}" + Class initialize method '{0}.{1}' timed out after {2}ms + Método de inicialización de clase '{0}.{1}' se agotó el tiempo de espera después de {2}ms @@ -72,8 +72,8 @@ pero recibió {4} argumento(s), con los tipos "{5}". - Test cleanup method '{0}.{1}' timed out - Se agotó el tiempo de espera del método de limpieza de pruebas "{0}.{1}" + Test cleanup method '{0}.{1}' timed out after {2}ms + Método de limpieza de pruebas '{0}.{1}' se agotó el tiempo de espera después de {2}ms @@ -82,8 +82,8 @@ pero recibió {4} argumento(s), con los tipos "{5}". - Test initialize method '{0}.{1}' timed out - Se agotó el tiempo de espera del método de inicialización de pruebas "{0}.{1}" + Test initialize method '{0}.{1}' timed out after {2}ms + Método de inicialización de prueba '{0}.{1}' se agotó el tiempo de espera después de {2}ms diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.fr.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.fr.xlf index 6d37b5443a..7fcbaff3ad 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.fr.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.fr.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - Méthode de nettoyage de l'assemblage '{0}.{1}' Fin du temps + Assembly cleanup method '{0}.{1}' timed out after {2}ms + La méthode de nettoyage d’assembly « {0}.{1} » a expiré après {2}ms @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - Le délai de la méthode d’initialisation de l’assembly « {0}.{1} » est dépassé + Assembly initialize method '{0}.{1}' timed out after {2}ms + La méthode d'initialisation de l’assembly « {0}.{1} » a expiré après {2}ms @@ -37,8 +37,8 @@ mais a reçu {4} argument(s), avec les types « {5} ». - Class cleanup method '{0}.{1}' timed out - Méthode de nettoyage de classe '{0}.{1}' a expiré + Class cleanup method '{0}.{1}' timed out after {2}ms + La méthode de nettoyage de classe « {0}.{1} » a expiré après {2}ms @@ -47,8 +47,8 @@ mais a reçu {4} argument(s), avec les types « {5} ». - Class initialize method '{0}.{1}' timed out - Le délai de la méthode d’initialisation de la classe « {0}.{1} » est dépassé + Class initialize method '{0}.{1}' timed out after {2}ms + La méthode d'initialisation de la classe « {0}.{1} » a expiré après {2}ms @@ -72,8 +72,8 @@ mais a reçu {4} argument(s), avec les types « {5} ». - Test cleanup method '{0}.{1}' timed out - Tester la méthode de nettoyage '{0}.{1}' a expiré + Test cleanup method '{0}.{1}' timed out after {2}ms + La méthode de nettoyage de test « {0}.{1} » a expiré après {2}ms @@ -82,8 +82,8 @@ mais a reçu {4} argument(s), avec les types « {5} ». - Test initialize method '{0}.{1}' timed out - Test initialiser la méthode '{0}.{1}' a expiré + Test initialize method '{0}.{1}' timed out after {2}ms + La méthode d’initialisation de test « {0}.{1} » a expiré après {2}ms diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.it.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.it.xlf index 33cd94ff9d..dc44fdc1a9 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.it.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.it.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - Il metodo di pulizia dell'assembly '{0}.{1}' è scaduto + Assembly cleanup method '{0}.{1}' timed out after {2}ms + Metodo di pulizia dell’assembly '{0}. Time out di {1}' dopo {2} ms @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - Timeout del metodo di inizializzazione dell'assembly "{0}.{1}" + Assembly initialize method '{0}.{1}' timed out after {2}ms + Metodo di inizializzazione dell'assembly '{0}. Timeout di {1}' dopo {2} ms @@ -37,8 +37,8 @@ ma ha ricevuto {4} argomenti, con tipi "{5}". - Class cleanup method '{0}.{1}' timed out - Il metodo di pulizia della classe '{0}.{1}' è scaduto + Class cleanup method '{0}.{1}' timed out after {2}ms + Time out del metodo di pulizia della classe '{0}. Time out di {1}' dopo {2} ms @@ -47,8 +47,8 @@ ma ha ricevuto {4} argomenti, con tipi "{5}". - Class initialize method '{0}.{1}' timed out - Timeout del metodo di inizializzazione della classe "{0}.{1}" + Class initialize method '{0}.{1}' timed out after {2}ms + Metodo di inizializzazione della classe '{0}. Timeout di {1}' dopo {2} ms @@ -72,8 +72,8 @@ ma ha ricevuto {4} argomenti, con tipi "{5}". - Test cleanup method '{0}.{1}' timed out - Il metodo di pulizia del test '{0}.{1}' è scaduto + Test cleanup method '{0}.{1}' timed out after {2}ms + Time out del metodo di pulizia della classe dei test '{0}. Time out di {1}' dopo {2} ms @@ -82,8 +82,8 @@ ma ha ricevuto {4} argomenti, con tipi "{5}". - Test initialize method '{0}.{1}' timed out - Il metodo di inizializzazione del test '{0}.{1}' è scaduto + Test initialize method '{0}.{1}' timed out after {2}ms + Metodo di inizializzazione del test '{0}. Timeout di {1}' dopo {2} ms diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ja.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ja.xlf index e43fa8f848..2e8d0ade3f 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ja.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ja.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - アセンブリ クリーンアップ メソッド '{0}.{1}' がタイムアウトしました + Assembly cleanup method '{0}.{1}' timed out after {2}ms + アセンブリ クリーンアップ メソッド '{0}.{1}' が {2}ms 後にタイムアウトしました @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - アセンブリ初期化メソッド '{0}.{1}' がタイムアウトになりました + Assembly initialize method '{0}.{1}' timed out after {2}ms + アセンブリ初期化メソッド '{0}.{1}' が {2}ms 後にタイムアウトになりました @@ -38,8 +38,8 @@ but received {4} argument(s), with types '{5}'. - Class cleanup method '{0}.{1}' timed out - クラス クリーンアップ メソッド '{0}.{1}' がタイムアウトしました + Class cleanup method '{0}.{1}' timed out after {2}ms + クラス クリーンアップ メソッド '{0}.{1}' が {2}ms 後にタイムアウトしました @@ -48,8 +48,8 @@ but received {4} argument(s), with types '{5}'. - Class initialize method '{0}.{1}' timed out - クラス初期化メソッド '{0}.{1}' がタイムアウトになりました + Class initialize method '{0}.{1}' timed out after {2}ms + クラス初期化メソッド '{0}.{1}' が {2}ms 後にタイムアウトになりました @@ -73,8 +73,8 @@ but received {4} argument(s), with types '{5}'. - Test cleanup method '{0}.{1}' timed out - テスト クリーンアップ メソッド '{0}.{1}' がタイムアウトしました + Test cleanup method '{0}.{1}' timed out after {2}ms + テスト クリーンアップ メソッド '{0}.{1}' が {2}ms 後にタイムアウトしました @@ -83,8 +83,8 @@ but received {4} argument(s), with types '{5}'. - Test initialize method '{0}.{1}' timed out - テスト初期化メソッド '{0}.{1}' がタイムアウトになりました + Test initialize method '{0}.{1}' timed out after {2}ms + テスト初期化メソッド '{0}.{1}' が {2}ms 後にタイムアウトになりました diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ko.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ko.xlf index a9aaa763f8..0e79807fb8 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ko.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ko.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - 어셈블리 정리 메서드 '{0}.{1}'이(가) 시간 초과되었습니다. + Assembly cleanup method '{0}.{1}' timed out after {2}ms + {2}밀리초 후 어셈블리 정리 메서드 '{0}.{1}'이(가) 시간 초과되었습니다. @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - '{0}.{1}' 어셈블리 초기화 메서드의 시간이 초과되었습니다. + Assembly initialize method '{0}.{1}' timed out after {2}ms + {2}밀리초 후 '{0}.{1}' 어셈블리 초기화 메서드의 시간이 초과되었습니다. @@ -37,8 +37,8 @@ but received {4} argument(s), with types '{5}'. - Class cleanup method '{0}.{1}' timed out - 클래스 정리 메서드 '{0}.{1}'이(가) 시간 초과되었습니다. + Class cleanup method '{0}.{1}' timed out after {2}ms + {2}밀리초 후 클래스 정리 메서드 '{0}.{1}'이(가) 시간 초과되었습니다. @@ -47,8 +47,8 @@ but received {4} argument(s), with types '{5}'. - Class initialize method '{0}.{1}' timed out - '{0}.{1}' 클래스 초기화 메서드의 시간이 초과되었습니다. + Class initialize method '{0}.{1}' timed out after {2}ms + {2}밀리초 후 '{0}.{1}' 클래스 초기화 메서드의 시간이 초과되었습니다. @@ -72,8 +72,8 @@ but received {4} argument(s), with types '{5}'. - Test cleanup method '{0}.{1}' timed out - 테스트 정리 메서드 '{0}.{1}'이(가) 시간 초과되었습니다. + Test cleanup method '{0}.{1}' timed out after {2}ms + {2}밀리초 후 테스트 정리 메서드 '{0}.{1}'이(가) 시간 초과되었습니다. @@ -82,8 +82,8 @@ but received {4} argument(s), with types '{5}'. - Test initialize method '{0}.{1}' timed out - '{0}.{1}' 테스트 초기화 메서드의 시간이 초과되었습니다. + Test initialize method '{0}.{1}' timed out after {2}ms + {2}밀리초 후 '{0}.{1}' 테스트 초기화 메서드의 시간이 초과되었습니다. diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pl.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pl.xlf index 8379ef1b1b..2f31550dc4 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pl.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pl.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - Upłynął limit czasu metody oczyszczania zestawu „{0}.{1}” + Assembly cleanup method '{0}.{1}' timed out after {2}ms + Metoda oczyszczania zestawu „{0}.{1}” przekroczyła limit czasu po {2}ms @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - Upłynął limit czasu metody inicjowania zestawu „{0}.{1}”. + Assembly initialize method '{0}.{1}' timed out after {2}ms + Metoda inicjalizacji zestawu „{0}.{1}” przekroczyła limit czasu po {2}ms @@ -37,8 +37,8 @@ ale liczba odebranych argumentów to {4} z typami „{5}”. - Class cleanup method '{0}.{1}' timed out - Upłynął limit czasu metody oczyszczania klasy „{0}.{1}” + Class cleanup method '{0}.{1}' timed out after {2}ms + Metoda oczyszczania klasy „{0}.{1}” przekroczyła limit czasu po {2}ms @@ -47,8 +47,8 @@ ale liczba odebranych argumentów to {4} z typami „{5}”. - Class initialize method '{0}.{1}' timed out - Upłynął limit czasu metody inicjowania klasy „{0}.{1}”. + Class initialize method '{0}.{1}' timed out after {2}ms + Metoda inicjalizacji klasy „{0}.{1}” przekroczyła limit czasu po {2}ms @@ -72,8 +72,8 @@ ale liczba odebranych argumentów to {4} z typami „{5}”. - Test cleanup method '{0}.{1}' timed out - Upłynął limit czasu metody oczyszczania testu „{0}.{1}” + Test cleanup method '{0}.{1}' timed out after {2}ms + Metoda oczyszczania testu „{0}.{1}” przekroczyła limit czasu po {2}ms @@ -82,8 +82,8 @@ ale liczba odebranych argumentów to {4} z typami „{5}”. - Test initialize method '{0}.{1}' timed out - Upłynął limit czasu metody inicjowania testu „{0}.{1}” + Test initialize method '{0}.{1}' timed out after {2}ms + Metoda inicjalizacji testu „{0}.{1}” przekroczyła limit czasu po {2}ms diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pt-BR.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pt-BR.xlf index 77747ba755..5068806ecf 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pt-BR.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pt-BR.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - O método de limpeza do assembly '{0}.{1}' atingiu o tempo limite + Assembly cleanup method '{0}.{1}' timed out after {2}ms + O método de limpeza da montagem '{0}.{1}' atingiu o tempo limite após {2}ms @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - Método de inicialização de montagem “{0}.{1}” tempo esgotado + Assembly initialize method '{0}.{1}' timed out after {2}ms + O método de inicialização da montagem '{0}.{1}' atingiu o tempo limite após {2}ms @@ -37,8 +37,8 @@ mas {4} argumentos recebidos, com tipos '{5}'. - Class cleanup method '{0}.{1}' timed out - O método de limpeza de classe '{0}.{1}' atingiu o tempo limite + Class cleanup method '{0}.{1}' timed out after {2}ms + O método de limpeza da classe '{0}.{1}' atingiu o tempo limite após {2}ms @@ -47,8 +47,8 @@ mas {4} argumentos recebidos, com tipos '{5}'. - Class initialize method '{0}.{1}' timed out - Método de inicialização da classe “{0}.{1}” tempo esgotado + Class initialize method '{0}.{1}' timed out after {2}ms + O método de inicialização da classe '{0}.{1}' atingiu o tempo limite após {2}ms @@ -72,8 +72,8 @@ mas {4} argumentos recebidos, com tipos '{5}'. - Test cleanup method '{0}.{1}' timed out - Testar o método de limpeza '{0}.{1}' atingiu o tempo limite + Test cleanup method '{0}.{1}' timed out after {2}ms + O método de limpeza do teste '{0}.{1}' atingiu o tempo limite após {2}ms @@ -82,8 +82,8 @@ mas {4} argumentos recebidos, com tipos '{5}'. - Test initialize method '{0}.{1}' timed out - Testar o método de inicialização '{0}.{1}' atingiu o tempo limite + Test initialize method '{0}.{1}' timed out after {2}ms + O método de inicialização do teste '{0}.{1}' atingiu o tempo limite após {2}ms diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ru.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ru.xlf index a5f6f03865..fffea6baae 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ru.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ru.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - Истекло время ожидания метода очистки сборки "{0}.{1}" + Assembly cleanup method '{0}.{1}' timed out after {2}ms + Время ожидания метода очистки сборки "{0}.{1}" истекло через {2} мс @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - Истекло время ожидания метода инициализации сборки "{0}.{1}" + Assembly initialize method '{0}.{1}' timed out after {2}ms + Время ожидания метода инициализации сборки "{0}.{1}" истекло через {2} мс @@ -37,8 +37,8 @@ but received {4} argument(s), with types '{5}'. - Class cleanup method '{0}.{1}' timed out - Истекло время ожидания метода очистки класса "{0}.{1}" + Class cleanup method '{0}.{1}' timed out after {2}ms + Время ожидания метода очистки класса "{0}.{1}" истекло через {2} мс @@ -47,8 +47,8 @@ but received {4} argument(s), with types '{5}'. - Class initialize method '{0}.{1}' timed out - Истекло время ожидания метода инициализации класса "{0}.{1}" + Class initialize method '{0}.{1}' timed out after {2}ms + Время ожидания метода инициализации класса "{0}.{1}" истекло через {2} мс @@ -72,8 +72,8 @@ but received {4} argument(s), with types '{5}'. - Test cleanup method '{0}.{1}' timed out - Истекло время ожидания метода очистки теста "{0}.{1}" + Test cleanup method '{0}.{1}' timed out after {2}ms + Время ожидания метода очистки теста "{0}.{1}" истекло через {2} мс @@ -82,8 +82,8 @@ but received {4} argument(s), with types '{5}'. - Test initialize method '{0}.{1}' timed out - Истекло время ожидания метода инициализации теста "{0}.{1}" + Test initialize method '{0}.{1}' timed out after {2}ms + Время ожидания метода инициализации теста "{0}.{1}" истекло через {2} мс diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.tr.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.tr.xlf index 3897bf34f7..9ad699359b 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.tr.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.tr.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - '{0}.{1}' bütünleştirilmiş kod temizleme yöntemi zaman aşımına uğradı + Assembly cleanup method '{0}.{1}' timed out after {2}ms + '{0}.{1}' derleme temizleme yöntemi {2}ms sonra zaman aşımına uğradı @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - '{0}.{1}' derleme başlatma yöntemi zaman aşımına uğradı + Assembly initialize method '{0}.{1}' timed out after {2}ms + '{0}.{1}' derleme başlatma yöntemi {2}ms sonra zaman aşımına uğradı @@ -37,8 +37,8 @@ ancak, '{5}' türüyle {4} argüman aldı. - Class cleanup method '{0}.{1}' timed out - '{0}.{1}' sınıf temizleme yöntemi zaman aşımına uğradı + Class cleanup method '{0}.{1}' timed out after {2}ms + '{0}.{1}' sınıf temizleme yöntemi {2}ms sonra zaman aşımına uğradı @@ -47,8 +47,8 @@ ancak, '{5}' türüyle {4} argüman aldı. - Class initialize method '{0}.{1}' timed out - '{0}.{1}' sınıf başlatma yöntemi zaman aşımına uğradı + Class initialize method '{0}.{1}' timed out after {2}ms + '{0}.{1}' sınıf başlatma yöntemi {2}ms sonra zaman aşımına uğradı @@ -72,8 +72,8 @@ ancak, '{5}' türüyle {4} argüman aldı. - Test cleanup method '{0}.{1}' timed out - '{0}.{1}' test temizleme yöntemi zaman aşımına uğradı + Test cleanup method '{0}.{1}' timed out after {2}ms + '{0}.{1}' test temizleme yöntemi {2}ms sonra zaman aşımına uğradı @@ -82,8 +82,8 @@ ancak, '{5}' türüyle {4} argüman aldı. - Test initialize method '{0}.{1}' timed out - '{0}.{1}' test başlatma yöntemi zaman aşımına uğradı + Test initialize method '{0}.{1}' timed out after {2}ms + '{0}.{1}' test başlatma yöntemi {2}ms sonra zaman aşımına uğradı diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hans.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hans.xlf index 8343c23cdb..90b86aa37c 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hans.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hans.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - 程序集清理方法“{0}.{1}”超时 + Assembly cleanup method '{0}.{1}' timed out after {2}ms + 程序集清理方法“{0}.{1}”在 {2} ms 后超时 @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - 程序集初始化方法“{0}.{1}”超时 + Assembly initialize method '{0}.{1}' timed out after {2}ms + 程序集初始化方法“{0}.{1}”在 {2} ms 后超时 @@ -37,8 +37,8 @@ but received {4} argument(s), with types '{5}'. - Class cleanup method '{0}.{1}' timed out - 类清理方法“{0}.{1}”超时 + Class cleanup method '{0}.{1}' timed out after {2}ms + 类清理方法“{0}.{1}”在 {2} ms 后超时 @@ -47,8 +47,8 @@ but received {4} argument(s), with types '{5}'. - Class initialize method '{0}.{1}' timed out - 类初始化方法“{0}.{1}”超时 + Class initialize method '{0}.{1}' timed out after {2}ms + 类初始化方法“{0}.{1}”在 {2} ms 后超时 @@ -72,8 +72,8 @@ but received {4} argument(s), with types '{5}'. - Test cleanup method '{0}.{1}' timed out - 测试清理方法“{0}.{1}”超时 + Test cleanup method '{0}.{1}' timed out after {2}ms + 测试清理方法“{0}.{1}”在 {2} ms 后超时 @@ -82,8 +82,8 @@ but received {4} argument(s), with types '{5}'. - Test initialize method '{0}.{1}' timed out - 测试初始化方法“{0}.{1}”超时 + Test initialize method '{0}.{1}' timed out after {2}ms + 测试初始化方法“{0}.{1}”在 {2} ms 后超时 diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hant.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hant.xlf index c2050c8b44..8a2fe5e8a7 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hant.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hant.xlf @@ -3,8 +3,8 @@ - Assembly cleanup method '{0}.{1}' timed out - 組件清理方法 '{0}.{1}' 已逾時 + Assembly cleanup method '{0}.{1}' timed out after {2}ms + 組件清理方法 '{0}.{1}' 在 {2} 毫秒後已逾時 @@ -13,8 +13,8 @@ - Assembly initialize method '{0}.{1}' timed out - 組件初始化方法 '{0}.{1}' 已逾時 + Assembly initialize method '{0}.{1}' timed out after {2}ms + 組件初始化方法 '{0}.{1}' 在 {2} 毫秒後已逾時 @@ -37,8 +37,8 @@ but received {4} argument(s), with types '{5}'. - Class cleanup method '{0}.{1}' timed out - 類別清理方法 '{0}.{1}' 已逾時 + Class cleanup method '{0}.{1}' timed out after {2}ms + 類別清理方法 '{0}.{1}' 在 {2} 毫秒後已逾時 @@ -47,8 +47,8 @@ but received {4} argument(s), with types '{5}'. - Class initialize method '{0}.{1}' timed out - 類別初始化方法 '{0}.{1}' 已逾時 + Class initialize method '{0}.{1}' timed out after {2}ms + 類別初始化方法 '{0}.{1}' 在 {2} 毫秒後已逾時 @@ -72,8 +72,8 @@ but received {4} argument(s), with types '{5}'. - Test cleanup method '{0}.{1}' timed out - 測試清理方法 '{0}.{1}' 已逾時 + Test cleanup method '{0}.{1}' timed out after {2}ms + 測試清理方法 '{0}.{1}' 在 {2} 毫秒後已逾時 @@ -82,8 +82,8 @@ but received {4} argument(s), with types '{5}'. - Test initialize method '{0}.{1}' timed out - 測試初始化方法 '{0}.{1}' 已逾時 + Test initialize method '{0}.{1}' timed out after {2}ms + 測試初始化方法 '{0}.{1}' 在 {2} 毫秒後已逾時 diff --git a/src/Adapter/MSTest.TestAdapter/RunConfigurationSettings.cs b/src/Adapter/MSTest.TestAdapter/RunConfigurationSettings.cs index 6d4279b25e..6e4d8d1c95 100644 --- a/src/Adapter/MSTest.TestAdapter/RunConfigurationSettings.cs +++ b/src/Adapter/MSTest.TestAdapter/RunConfigurationSettings.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics.CodeAnalysis; using System.Xml; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; @@ -44,7 +45,7 @@ public RunConfigurationSettings() /// Populated RunConfigurationSettings from the discovery context. public static RunConfigurationSettings PopulateSettings(IDiscoveryContext? context) { - if (context == null || context.RunSettings == null || StringEx.IsNullOrEmpty(context.RunSettings.SettingsXml)) + if (context?.RunSettings == null || StringEx.IsNullOrEmpty(context.RunSettings.SettingsXml)) { // This will contain default configuration settings return new RunConfigurationSettings(); @@ -61,7 +62,9 @@ public static RunConfigurationSettings PopulateSettings(IDiscoveryContext? conte /// The xml with the settings passed from the test platform. /// The name of the settings to fetch. /// The settings if found. Null otherwise. - internal static RunConfigurationSettings? GetSettings(string runsettingsXml, string settingName) + internal static RunConfigurationSettings? GetSettings( + [StringSyntax(StringSyntaxAttribute.Xml, nameof(runsettingsXml))] string runsettingsXml, + string settingName) { using var stringReader = new StringReader(runsettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); diff --git a/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs b/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs index 8d6ab3bc9f..b967f19dbc 100644 --- a/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs +++ b/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Reflection; @@ -91,6 +91,17 @@ internal TestProperty PropertyProvider(string propertyName) return currentTest.GetPropertyValue(testProperty); } } + else + { + // Everything that it's not a supported property we use traits + foreach (Trait trait in currentTest.Traits) + { + if (trait.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)) + { + return trait.Value; + } + } + } } return null; @@ -115,7 +126,7 @@ internal TestProperty PropertyProvider(string propertyName) { // GetTestCaseFilter is present in DiscoveryContext but not in IDiscoveryContext interface. MethodInfo? methodGetTestCaseFilter = context.GetType().GetRuntimeMethod("GetTestCaseFilter", [typeof(IEnumerable), typeof(Func)]); - return (ITestCaseFilterExpression?)methodGetTestCaseFilter?.Invoke(context, new object[] { _supportedProperties.Keys, (Func)PropertyProvider }); + return (ITestCaseFilterExpression?)methodGetTestCaseFilter?.Invoke(context, [_supportedProperties.Keys, (Func)PropertyProvider]); } catch (Exception ex) { diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBannerCapability.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBannerCapability.cs new file mode 100644 index 0000000000..af5c0c0e4b --- /dev/null +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBannerCapability.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if !WINDOWS_UWP +using System.Runtime.InteropServices; +using System.Text; + +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.VisualStudio.TestTools.UnitTesting; + +#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. +internal sealed class MSTestBannerCapability : IBannerMessageOwnerCapability +{ + private readonly IPlatformInformation _platformInformation; + + public MSTestBannerCapability(IPlatformInformation platformInformation) => _platformInformation = platformInformation; + + public Task GetBannerMessageAsync() + { + StringBuilder bannerMessage = new(); + bannerMessage.Append("MSTest v"); + bannerMessage.Append(MSTestVersion.SemanticVersion); + + if (_platformInformation.BuildDate is { } buildDate) + { + bannerMessage.Append(" (UTC "); + bannerMessage.Append(buildDate.UtcDateTime.ToShortDateString()); + bannerMessage.Append(')'); + } + +#if NETCOREAPP + if (System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeCompiled) +#endif + { + bannerMessage.Append(" ["); +#if NET6_0_OR_GREATER + bannerMessage.Append(RuntimeInformation.RuntimeIdentifier); +#else + bannerMessage.Append(RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant()); +#endif + bannerMessage.Append(" - "); + bannerMessage.Append(RuntimeInformation.FrameworkDescription); + bannerMessage.Append(']'); + } + + return Task.FromResult(bannerMessage.ToString()); + } +} +#endif diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestApplicationBuilderExtensions.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestApplicationBuilderExtensions.cs index 2fdca76fbc..d1b31310d5 100644 --- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestApplicationBuilderExtensions.cs +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestApplicationBuilderExtensions.cs @@ -8,6 +8,7 @@ using Microsoft.Testing.Extensions.VSTestBridge.Helpers; using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Services; namespace Microsoft.VisualStudio.TestTools.UnitTesting; @@ -18,8 +19,13 @@ public static void AddMSTest(this ITestApplicationBuilder testApplicationBuilder MSTestExtension extension = new(); testApplicationBuilder.AddRunSettingsService(extension); testApplicationBuilder.AddTestCaseFilterService(extension); + testApplicationBuilder.AddTestRunParametersService(extension); testApplicationBuilder.RegisterTestFramework( - _ => new TestFrameworkCapabilities(new VSTestBridgeExtensionBaseCapabilities()), + serviceProvider => new TestFrameworkCapabilities( + new VSTestBridgeExtensionBaseCapabilities(), +#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. + new MSTestBannerCapability(serviceProvider.GetRequiredService())), +#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. (capabilities, serviceProvider) => new MSTestBridgedTestFramework(extension, getTestAssemblies, serviceProvider, capabilities)); } } diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestingPlatformBuilderHook.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestingPlatformBuilderHook.cs index 4897215fad..befda48370 100644 --- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestingPlatformBuilderHook.cs +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestingPlatformBuilderHook.cs @@ -11,6 +11,6 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; public static class TestingPlatformBuilderHook { #pragma warning disable IDE0060 // Remove unused parameter - public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] arguments) => testApplicationBuilder.AddMSTest(() => new[] { Assembly.GetEntryAssembly()! }); + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] arguments) => testApplicationBuilder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); } #endif diff --git a/src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs b/src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs index 9f8eb71a73..e5c0fbaa1f 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs @@ -86,6 +86,7 @@ class AssemblyResolver : /// private readonly object _syncLock = new(); + private static List? s_currentlyLoading; private bool _disposed; /// @@ -105,15 +106,15 @@ public AssemblyResolver(IList directories) throw new ArgumentNullException(nameof(directories)); } - _searchDirectories = new List(directories); + _searchDirectories = [.. directories]; _directoryList = new Queue(); - AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(OnResolve); + AppDomain.CurrentDomain.AssemblyResolve += OnResolve; #if NETFRAMEWORK - AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(ReflectionOnlyOnResolve); + AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ReflectionOnlyOnResolve; // This is required for winmd resolution for arm built sources discovery on desktop. - WindowsRuntimeMetadata.ReflectionOnlyNamespaceResolve += new EventHandler(WindowsRuntimeMetadataReflectionOnlyNamespaceResolve); + WindowsRuntimeMetadata.ReflectionOnlyNamespaceResolve += WindowsRuntimeMetadataReflectionOnlyNamespaceResolve; #endif } @@ -242,11 +243,11 @@ void Dispose(bool disposing) if (disposing) { // cleanup Managed resources like calling dispose on other managed object created. - AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(OnResolve); + AppDomain.CurrentDomain.AssemblyResolve -= OnResolve; #if NETFRAMEWORK - AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= new ResolveEventHandler(ReflectionOnlyOnResolve); - WindowsRuntimeMetadata.ReflectionOnlyNamespaceResolve -= new EventHandler(WindowsRuntimeMetadataReflectionOnlyNamespaceResolve); + AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= ReflectionOnlyOnResolve; + WindowsRuntimeMetadata.ReflectionOnlyNamespaceResolve -= WindowsRuntimeMetadataReflectionOnlyNamespaceResolve; #endif } @@ -319,7 +320,7 @@ protected virtual } // args.Name is like: "Microsoft.VisualStudio.TestTools.Common, Version=[VersionMajor].0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a". - AssemblyName? requestedName = null; + AssemblyName? requestedName; try { @@ -335,7 +336,7 @@ protected virtual if (EqtTrace.IsInfoEnabled) { EqtTrace.Info( - "AssemblyResolver: {0}: Failed to create assemblyName. Reason:{1} ", + "MSTest.AssemblyResolver.OnResolve: Failed to create assemblyName '{0}'. Reason: {1} ", name, ex); } @@ -344,7 +345,7 @@ protected virtual return null; } - DebugEx.Assert(requestedName != null && !StringEx.IsNullOrEmpty(requestedName.Name), "AssemblyResolver.OnResolve: requested is null or name is empty!"); + DebugEx.Assert(requestedName != null && !StringEx.IsNullOrEmpty(requestedName.Name), "MSTest.AssemblyResolver.OnResolve: requested is null or name is empty!"); foreach (string dir in searchDirectorypaths) { @@ -359,7 +360,7 @@ protected virtual { if (EqtTrace.IsVerboseEnabled) { - EqtTrace.Verbose("AssemblyResolver: Searching assembly: {0} in the directory: {1}", requestedName.Name, dir); + EqtTrace.Verbose("MSTest.AssemblyResolver.OnResolve: Searching assembly '{0}' in the directory '{1}'", requestedName.Name, dir); } }); @@ -367,7 +368,31 @@ protected virtual { string assemblyPath = Path.Combine(dir, requestedName.Name + extension); + bool isPushed = false; + bool isResource = requestedName.Name.EndsWith(".resources", StringComparison.InvariantCulture); + if (isResource) + { + // Are we recursively looking up the same resource? Note - our backout code will set + // the ResourceHelper's currentlyLoading stack to null if an exception occurs. + if (s_currentlyLoading != null && s_currentlyLoading.Count > 0 && s_currentlyLoading.LastIndexOf(assemblyPath) != -1) + { + EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Assembly '{0}' is searching for itself recursively '{1}', returning as not found.", name, assemblyPath); + _resolvedAssemblies[name] = null; + return null; + } + + s_currentlyLoading ??= new List(); + s_currentlyLoading.Add(assemblyPath); // Push + isPushed = true; + } + Assembly? assembly = SearchAndLoadAssembly(assemblyPath, name, requestedName, isReflectionOnly); + if (isResource && isPushed) + { + DebugEx.Assert(s_currentlyLoading is not null, "_currentlyLoading should not be null"); + s_currentlyLoading.RemoveAt(s_currentlyLoading.Count - 1); // Pop + } + if (assembly != null) { return assembly; @@ -447,7 +472,7 @@ private void WindowsRuntimeMetadataReflectionOnlyNamespaceResolve(object sender, { if (StringEx.IsNullOrEmpty(args.Name)) { - Debug.Fail("AssemblyResolver.OnResolve: args.Name is null or empty."); + Debug.Fail("MSTest.AssemblyResolver.OnResolve: args.Name is null or empty."); return null; } @@ -457,7 +482,7 @@ private void WindowsRuntimeMetadataReflectionOnlyNamespaceResolve(object sender, { if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("AssemblyResolver: Resolving assembly: {0}.", args.Name); + EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Resolving assembly '{0}'", args.Name); } }); @@ -469,7 +494,7 @@ private void WindowsRuntimeMetadataReflectionOnlyNamespaceResolve(object sender, { if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("AssemblyResolver: Resolving assembly after applying policy: {0}.", assemblyNameToLoad); + EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Resolving assembly after applying policy '{0}'", assemblyNameToLoad); } }); @@ -482,62 +507,58 @@ private void WindowsRuntimeMetadataReflectionOnlyNamespaceResolve(object sender, } assembly = SearchAssembly(_searchDirectories, assemblyNameToLoad, isReflectionOnly); - if (assembly != null) { return assembly; } - if (_directoryList != null && _directoryList.Count != 0) + // required assembly is not present in searchDirectories?? + // see, if we can find it in user specified search directories. + while (assembly == null && _directoryList?.Count > 0) { - // required assembly is not present in searchDirectories?? - // see, if we can find it in user specified search directories. - while (assembly == null && _directoryList.Count > 0) - { - // instead of loading whole search directory in one time, we are adding directory on the basis of need - RecursiveDirectoryPath currentNode = _directoryList.Dequeue(); - - List incrementalSearchDirectory = []; + // instead of loading whole search directory in one time, we are adding directory on the basis of need + RecursiveDirectoryPath currentNode = _directoryList.Dequeue(); - if (DoesDirectoryExist(currentNode.DirectoryPath)) - { - incrementalSearchDirectory.Add(currentNode.DirectoryPath); - - if (currentNode.IncludeSubDirectories) - { - // Add all its sub-directory in depth first search order. - AddSubdirectories(currentNode.DirectoryPath, incrementalSearchDirectory); - } + List incrementalSearchDirectory = []; - // Add this directory list in this.searchDirectories so that when we will try to resolve some other - // assembly, then it will look in this whole directory first. - _searchDirectories.AddRange(incrementalSearchDirectory); + if (DoesDirectoryExist(currentNode.DirectoryPath)) + { + incrementalSearchDirectory.Add(currentNode.DirectoryPath); - assembly = SearchAssembly(incrementalSearchDirectory, assemblyNameToLoad, isReflectionOnly); - } - else + if (currentNode.IncludeSubDirectories) { - // generate warning that path does not exist. - SafeLog( - assemblyNameToLoad, - () => - { - if (EqtTrace.IsWarningEnabled) - { - EqtTrace.Warning( - "The Directory: {0}, does not exist", - currentNode.DirectoryPath); - } - }); + // Add all its sub-directory in depth first search order. + AddSubdirectories(currentNode.DirectoryPath, incrementalSearchDirectory); } - } - if (assembly != null) + // Add this directory list in this.searchDirectories so that when we will try to resolve some other + // assembly, then it will look in this whole directory first. + _searchDirectories.AddRange(incrementalSearchDirectory); + + assembly = SearchAssembly(incrementalSearchDirectory, assemblyNameToLoad, isReflectionOnly); + } + else { - return assembly; + // generate warning that path does not exist. + SafeLog( + assemblyNameToLoad, + () => + { + if (EqtTrace.IsWarningEnabled) + { + EqtTrace.Warning( + "MSTest.AssemblyResolver.OnResolve: the directory '{0}', does not exist", + currentNode.DirectoryPath); + } + }); } } + if (assembly != null) + { + return assembly; + } + // Try for default load for System dlls that can't be found in search paths. Needs to loaded just by name. try { @@ -580,7 +601,7 @@ private void WindowsRuntimeMetadataReflectionOnlyNamespaceResolve(object sender, { if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("AssemblyResolver: {0}: Failed to load assembly. Reason: {1}", assemblyNameToLoad, ex); + EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Failed to load assembly '{0}'. Reason: {1}", assemblyNameToLoad, ex); } }); } @@ -609,7 +630,7 @@ private bool TryLoadFromCache(string assemblyName, bool isReflectionOnly, out As { if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("AssemblyResolver: Resolved: {0}.", assemblyName); + EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Resolved '{0}'", assemblyName); } }); return true; @@ -685,7 +706,7 @@ private static void SafeLog(string? assemblyName, Action loggerAction) { if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("AssemblyResolver: Resolved assembly: {0}. ", assemblyName); + EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Resolved assembly '{0}'", assemblyName); } }); @@ -699,7 +720,7 @@ private static void SafeLog(string? assemblyName, Action loggerAction) { if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("AssemblyResolver: Failed to load assembly: {0}. Reason:{1} ", assemblyName, ex); + EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Failed to load assembly '{0}'. Reason:{1} ", assemblyName, ex); } }); @@ -717,7 +738,7 @@ private static void SafeLog(string? assemblyName, Action loggerAction) { if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("AssemblyResolver: Failed to load assembly: {0}. Reason:{1} ", assemblyName, ex); + EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Failed to load assembly '{0}'. Reason:{1} ", assemblyName, ex); } }); } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Data/CsvDataConnection.cs b/src/Adapter/MSTestAdapter.PlatformServices/Data/CsvDataConnection.cs index 4e4cfbfa89..c3b5eadf7b 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Data/CsvDataConnection.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Data/CsvDataConnection.cs @@ -39,10 +39,10 @@ public CsvDataConnection(string fileName, List dataFolders) public override List GetDataTablesAndViews() { - List tableNames = new(1) - { - TableName, - }; + List tableNames = + [ + TableName + ]; return tableNames; } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Data/TestDataConnectionSql.cs b/src/Adapter/MSTestAdapter.PlatformServices/Data/TestDataConnectionSql.cs index 9524431ecc..1f9ca7507c 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Data/TestDataConnectionSql.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Data/TestDataConnectionSql.cs @@ -193,7 +193,7 @@ public string PrepareNameForSql(string tableName) { string[]? parts = SplitName(tableName); - if (parts != null && parts.Length > 0) + if (parts is { Length: > 0 }) { // Seems to be well formed, so make sure we end up fully quoted return JoinAndQuoteName(parts, true); @@ -356,9 +356,9 @@ protected void GetQuoteLiteralsHelper() // Try to get quote chars by hand for those providers that for some reason return empty QuotePrefix/Suffix. string s = "abcdefgh"; string quoted = QuoteIdentifier(s); - string[] parts = quoted.Split(new string[] { s }, StringSplitOptions.None); + string[] parts = quoted.Split([s], StringSplitOptions.None); - DebugEx.Assert(parts != null && parts.Length == 2, "TestDataConnectionSql.GetQuotesLiteralHelper: Failure when trying to quote an identifier!"); + DebugEx.Assert(parts is { Length: 2 }, "TestDataConnectionSql.GetQuotesLiteralHelper: Failure when trying to quote an identifier!"); DebugEx.Assert(!StringEx.IsNullOrEmpty(parts[0]), "TestDataConnectionSql.GetQuotesLiteralHelper: Trying to set empty value for QuotePrefix!"); DebugEx.Assert(!StringEx.IsNullOrEmpty(parts[1]), "TestDataConnectionSql.GetQuotesLiteralHelper: Trying to set empty value for QuoteSuffix!"); @@ -687,7 +687,7 @@ private static bool IsInArray(string? candidate, string[]? values) #region Helpers #pragma warning disable SA1202 // Elements must be ordered by access - public bool IsOpen() => _connection != null && _connection.State == ConnectionState.Open; + public bool IsOpen() => _connection is { State: ConnectionState.Open }; /// /// Returns true when given provider (OLEDB or ODBC) is for MSSql. diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Data/XmlDataConnection.cs b/src/Adapter/MSTestAdapter.PlatformServices/Data/XmlDataConnection.cs index 2c4cb9e1d5..e3bd7f4658 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Data/XmlDataConnection.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Data/XmlDataConnection.cs @@ -51,12 +51,8 @@ public XmlDataConnection(string fileName, List dataFolders) public override List? GetColumns(string tableName) { DataSet? dataSet = LoadDataSet(true); - if (dataSet == null) - { - return null; - } - DataTable table = dataSet.Tables[tableName]; + DataTable? table = dataSet?.Tables[tableName]; if (table == null) { return null; diff --git a/src/Adapter/MSTestAdapter.PlatformServices/MSTestAdapter.PlatformServices.csproj b/src/Adapter/MSTestAdapter.PlatformServices/MSTestAdapter.PlatformServices.csproj index aec221ae0a..d24106efa8 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/MSTestAdapter.PlatformServices.csproj +++ b/src/Adapter/MSTestAdapter.PlatformServices/MSTestAdapter.PlatformServices.csproj @@ -5,7 +5,7 @@ - $(UwpMinimum);$(WinUiMinimum);netstandard2.0;$(NetFrameworkMinimum);$(SupportedNetFrameworks) + netstandard2.0;$(NetFrameworkMinimum);$(SupportedNetFrameworks);$(UwpMinimum);$(WinUiMinimum) $(SupportedNetFrameworks);netstandard2.0 enable true diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/AssemblyExecutionContextScope.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/AssemblyExecutionContextScope.cs new file mode 100644 index 0000000000..910206ccba --- /dev/null +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/AssemblyExecutionContextScope.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; + +internal readonly struct AssemblyExecutionContextScope : IExecutionContextScope +{ + public AssemblyExecutionContextScope(bool isCleanup) + { + IsCleanup = isCleanup; + } + + public bool IsCleanup { get; } +} diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/ClassExecutionContextScope.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/ClassExecutionContextScope.cs new file mode 100644 index 0000000000..4886a77b32 --- /dev/null +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/ClassExecutionContextScope.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; + +internal readonly struct ClassExecutionContextScope : IExecutionContextScope +{ + public ClassExecutionContextScope(Type type) + { + Type = type; + IsCleanup = false; + RemainingCleanupCount = 0; + } + + public ClassExecutionContextScope(Type type, int remainingCleanupCount) + { + Type = type; + IsCleanup = true; + RemainingCleanupCount = remainingCleanupCount; + } + + public Type Type { get; } + + public bool IsCleanup { get; } + + public int RemainingCleanupCount { get; } + + public override int GetHashCode() => Type.GetHashCode(); + + public override bool Equals(object? obj) => Type.Equals(obj); +} diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/DiaSessionOperations.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/DiaSessionOperations.cs index 4249433037..a766a0fc9d 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/DiaSessionOperations.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/DiaSessionOperations.cs @@ -61,7 +61,7 @@ internal static void GetNavigationData(object? navigationSession, string classNa if (s_typeDiaSession != null && s_typeDiaNavigationData != null) { string messageFormatOnException = string.Join("MSTestDiscoverer:DiaSession: Could not get navigation data for class:", className, ". Reason:{0}"); - object? data = SafeInvoke(() => s_methodGetNavigationData!.Invoke(navigationSession, new object[] { className, methodName })); + object? data = SafeInvoke(() => s_methodGetNavigationData!.Invoke(navigationSession, [className, methodName])); if (data != null) { diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/ExecutionContextService.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/ExecutionContextService.cs new file mode 100644 index 0000000000..c38800d1c0 --- /dev/null +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/ExecutionContextService.cs @@ -0,0 +1,192 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Concurrent; +using System.Diagnostics; + +namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; + +internal static class ExecutionContextService +{ + /// + /// The execution context to use by class fixtures (ClassInitialize and ClassCleanup). + /// + /// The type used as key is the type of the test class and not the type of the method info, this is ensuring that mutations + /// done in parent classes are correctly impacting only the current context class. + /// + /// The logic for the context associated to the key is as follows: + /// - Copy and reuse the assembly level context, + /// - If not available, capture the current context and use it. + /// After each ClassInitialize or ClassCleanup, mutate (recapture) the context for the class. + /// + private static readonly ConcurrentDictionary ClassesExecutionContexts = new(); + + /// + /// The execution context to use for instance methods fixtures and tests (TestInitialize, TestMethod, TestCleanup). + /// + /// The key is the instance of the test class. + /// + /// The logic for the context associated to the key is as follows: + /// - Copy and reuse the class level context, + /// - If not available, reuse the assembly level context, + /// - If not available, capture the current context and use it. + /// After each TestInitialize or TestCleanup, mutate (recapture) the context for the instance. + /// + private static readonly ConcurrentDictionary InstancesExecutionContexts = new(); + + /// + /// As we only support one assembly level context, we store it here. + /// + private static ExecutionContext? s_assemblyExecutionContext; + + /// + /// When we execute the action, we need to ensure we are restoring the execution context that was captured in the logical flow of execution. + /// After the action is executed we capture the current execution context and save it for the next action to use based on the current execution context scope. + /// + /// The logical flow of execution is: + /// - AssemblyInitialize execution context is saved at "assembly level" and a copy is flown to each ClassInitialize. + /// - ClassInitialize execution context is saved at "class level" and a copy is flown to each TestInitialize/TestMethod/TestCleanup. + /// - TestInitialize/TestMethod/TestCleanup execution context is mutating the "instance level" + /// - ClassCleanup reuses the "class level" execution context. + /// - AssemblyCleanup reuses the "assembly level" execution context. + /// + internal static void RunActionOnContext(Action action, IExecutionContextScope executionContextScope) + { + // TODO: Log (trace/debug) the execution context scope and the current execution context. + // This would be particularly useful if we have a strange context issue to understand what is being set or not, + // What we manage to capture and what we don't, etc. + if (GetScopedExecutionContext(executionContextScope) is not { } executionContext) + { + // We don't have any execution context (that's usually the case when it is being suppressed), so we can run the action directly. + action(); + return; + } + + // We have an execution context, so we need to run the action in that context to ensure the flow of execution is preserved. + ExecutionContext.Run( + executionContext, + _ => + { + action(); + + if (ShouldCleanup(executionContextScope)) + { + CleanupExecutionContext(executionContextScope); + } + else + { + // The execution context and synchronization contexts of the calling thread are returned to their previous + // states when the method completes. That's why we need to capture the state and mutate the state before exiting. + SaveExecutionContext(executionContextScope); + } + }, + null); + } + + /// + /// Capture the new state of the execution context and mutate the right variable/dictionary based on the execution context scope. + /// + private static void SaveExecutionContext(IExecutionContextScope executionContextScope) + { + var capturedContext = ExecutionContext.Capture(); + switch (executionContextScope) + { + case AssemblyExecutionContextScope: + s_assemblyExecutionContext = capturedContext; + break; + + case ClassExecutionContextScope classExecutionContextScope: + ClassesExecutionContexts.AddOrUpdate( + classExecutionContextScope.Type, + _ => capturedContext, + (_, _) => capturedContext); + break; + + case InstanceExecutionContextScope instanceExecutionContextScope: + InstancesExecutionContexts.AddOrUpdate( + instanceExecutionContextScope.Instance, + _ => capturedContext, + (_, _) => capturedContext); + break; + } + } + + /// + /// Clears up the backed up execution state based on the execution context scope. + /// + private static void CleanupExecutionContext(IExecutionContextScope executionContextScope) + { + Debug.Assert(executionContextScope.IsCleanup, "CleanupExecutionContext should be called only in a cleanup scope."); + + switch (executionContextScope) + { + case AssemblyExecutionContextScope: + // When calling the assembly cleanup, we can clear up all the contexts that would not have been cleaned up. + foreach (ExecutionContext? context in InstancesExecutionContexts.Values) + { + context?.Dispose(); + } + + foreach (ExecutionContext? context in ClassesExecutionContexts.Values) + { + context?.Dispose(); + } + + InstancesExecutionContexts.Clear(); + ClassesExecutionContexts.Clear(); + s_assemblyExecutionContext?.Dispose(); + s_assemblyExecutionContext = null; + break; + + case ClassExecutionContextScope classExecutionContextScope: + _ = ClassesExecutionContexts.TryRemove(classExecutionContextScope.Type, out ExecutionContext? classContext); + classContext?.Dispose(); + break; + + case InstanceExecutionContextScope instanceExecutionContextScope: + _ = InstancesExecutionContexts.TryRemove(instanceExecutionContextScope.Instance, out ExecutionContext? instanceContext); + instanceContext?.Dispose(); + break; + } + } + + private static ExecutionContext? GetScopedExecutionContext(IExecutionContextScope executionContextScope) + { + ExecutionContext? executionContext = executionContextScope switch + { + // Return the assembly level context or capture and save it if it doesn't exist. + AssemblyExecutionContextScope => s_assemblyExecutionContext ??= ExecutionContext.Capture(), + + // Return the class level context or if it doesn't exist do the following steps: + // - use the assembly level context if it exists + // - or capture and save current context + ClassExecutionContextScope classExecutionContextScope => ClassesExecutionContexts.GetOrAdd( + classExecutionContextScope.Type, + _ => s_assemblyExecutionContext ?? ExecutionContext.Capture()), + + // Return the instance level context or if it doesn't exist do the following steps: + // - use the class level context if it exists + // - or use the assembly level context if it exists + // - or capture and save current context + InstanceExecutionContextScope instanceExecutionContextScope => InstancesExecutionContexts.GetOrAdd( + instanceExecutionContextScope.Instance, + _ => ClassesExecutionContexts.TryGetValue(instanceExecutionContextScope.Type, out ExecutionContext? classExecutionContext) + ? classExecutionContext + : s_assemblyExecutionContext ?? ExecutionContext.Capture()), + _ => throw new NotSupportedException($"Unsupported execution context scope: {executionContextScope.GetType()}"), + }; + + // Always create a copy of the context because running twice on the same context results in an error. + return executionContext?.CreateCopy(); + } + + private static bool ShouldCleanup(this IExecutionContextScope executionContextScope) + => executionContextScope.IsCleanup + && executionContextScope switch + { + AssemblyExecutionContextScope => true, + ClassExecutionContextScope classExecutionContextScope => classExecutionContextScope.RemainingCleanupCount == 0, + InstanceExecutionContextScope instanceExecutionContext => instanceExecutionContext.RemainingCleanupCount == 0, + _ => throw new NotSupportedException($"Unsupported execution context scope: {executionContextScope.GetType()}"), + }; +} diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/FileOperations.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/FileOperations.cs index 65100a103c..46275bb72f 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/FileOperations.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/FileOperations.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Collections.Concurrent; using System.Reflection; #if WIN_UI @@ -19,6 +20,8 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// public class FileOperations : IFileOperations { + private readonly ConcurrentDictionary _assemblyCache = new(); + #if WIN_UI private readonly bool _isPackaged; @@ -45,11 +48,19 @@ public Assembly LoadAssembly(string assemblyName, bool isReflectionOnly) } #endif string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(assemblyName); - return Assembly.Load(new AssemblyName(fileNameWithoutExtension)); + Assembly assembly = _assemblyCache.GetOrAdd(fileNameWithoutExtension, fileNameWithoutExtension => Assembly.Load(new AssemblyName(fileNameWithoutExtension))); + + return assembly; #elif NETFRAMEWORK -#pragma warning disable IDE0022 // Use expression body for method - return isReflectionOnly ? Assembly.ReflectionOnlyLoadFrom(assemblyName) : Assembly.LoadFrom(assemblyName); -#pragma warning restore IDE0022 // Use expression body for method + if (isReflectionOnly) + { + return Assembly.ReflectionOnlyLoadFrom(assemblyName); + } + else + { + Assembly assembly = _assemblyCache.GetOrAdd(assemblyName, Assembly.LoadFrom); + return assembly; + } #endif } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/IExecutionContextScope.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/IExecutionContextScope.cs new file mode 100644 index 0000000000..7119d94a5c --- /dev/null +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/IExecutionContextScope.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; + +internal interface IExecutionContextScope +{ + public bool IsCleanup { get; } +} diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/InstanceExecutionContextScope.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/InstanceExecutionContextScope.cs new file mode 100644 index 0000000000..99229a4d7a --- /dev/null +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/InstanceExecutionContextScope.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; + +internal readonly struct InstanceExecutionContextScope : IExecutionContextScope +{ + public InstanceExecutionContextScope(object instance, Type type) + { + Instance = instance; + Type = type; + IsCleanup = false; + RemainingCleanupCount = 0; + } + + public InstanceExecutionContextScope(object instance, Type type, int remainingCleanupCount) + { + Instance = instance; + Type = type; + IsCleanup = true; + RemainingCleanupCount = remainingCleanupCount; + } + + public object Instance { get; } + + public Type Type { get; } + + public bool IsCleanup { get; } + + public int RemainingCleanupCount { get; } + + public override int GetHashCode() => Instance.GetHashCode(); + + public override bool Equals(object? obj) => Instance.Equals(obj); +} diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs index bca31817aa..4c76e1262f 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs @@ -58,6 +58,7 @@ public static MSTestAdapterSettings ToSettings(XmlReader reader) // // true // true + // true // true // // diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs index ebfefedc45..74c88e6d38 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs @@ -134,7 +134,7 @@ public bool Deploy(IEnumerable tests, IRunContext? runContext, IFramew _adapterSettings = MSTestSettingsProvider.Settings; bool canDeploy = CanDeploy(); - bool hasDeploymentItems = tests.Any(test => DeploymentItemUtility.HasDeploymentItems(test)); + bool hasDeploymentItems = tests.Any(DeploymentItemUtility.HasDeploymentItems); // deployment directories should not be created in this case,simply return if (!canDeploy && hasDeploymentItems) diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs index d7f1254272..30b64ac6c9 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs @@ -102,14 +102,8 @@ internal TestSourceHost(string sourceFileName, IRunSettings? runSettings, IFrame /// public void SetupHost() { -#if NETFRAMEWORK || NET - List resolutionPaths = GetResolutionPaths( - _sourceFileName, -#if NETFRAMEWORK - VSInstallationUtilities.IsCurrentProcessRunningInPortableMode()); -#else - false); -#endif +#if NET + List resolutionPaths = GetResolutionPaths(_sourceFileName, false); if (EqtTrace.IsInfoEnabled) { @@ -125,10 +119,20 @@ public void SetupHost() { assemblyResolver.Dispose(); } +#elif NETFRAMEWORK + List resolutionPaths = GetResolutionPaths(_sourceFileName, VSInstallationUtilities.IsCurrentProcessRunningInPortableMode()); -#endif + if (EqtTrace.IsInfoEnabled) + { + EqtTrace.Info("DesktopTestSourceHost.SetupHost(): Creating assembly resolver with resolution paths {0}.", string.Join(",", resolutionPaths)); + } + + // NOTE: These 2 lines are super important, see https://github.com/microsoft/testfx/issues/2922 + // It's not entirely clear why but not assigning directly the resolver to the field (or/and) disposing the resolver in + // case of an error in TryAddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver causes the issue. + _parentDomainAssemblyResolver = new AssemblyResolver(resolutionPaths); + _ = TryAddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(_parentDomainAssemblyResolver, Path.GetDirectoryName(_sourceFileName)!); -#if NETFRAMEWORK // Case when DisableAppDomain setting is present in runsettings and no child-appdomain needs to be created if (!_isAppDomainCreationDisabled) { @@ -150,6 +154,12 @@ public void SetupHost() // which is trigged by AppContainerUtilities.AttachEventToResolveWinmd method. EqtTrace.SetupRemoteEqtTraceListeners(AppDomain); + // Force loading Microsoft.TestPlatform.CoreUtilities in the new app domain to ensure there is no assembly resolution issue. + // For unknown reasons, with MSTest 3.4+ we start to see infinite cycles of assembly resolution of this dll in the new app + // domain. In older versions, this was not the case, and the callback was allowing to fully lookup and load the dll before + // triggering the next resolution. + AppDomain.Load(typeof(EqtTrace).Assembly.GetName()); + // Add an assembly resolver in the child app-domain... Type assemblyResolverType = typeof(AssemblyResolver); @@ -158,7 +168,7 @@ public void SetupHost() object resolver = AppDomainUtilities.CreateInstance( AppDomain, assemblyResolverType, - new object[] { resolutionPaths }); + [resolutionPaths]); EqtTrace.Info( "DesktopTestSourceHost.SetupHost(): resolver type: {0} , resolve type assembly: {1} ", @@ -403,7 +413,7 @@ private static bool TryAddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResol try { List additionalSearchDirectories = adapterSettings.GetDirectoryListWithRecursiveProperty(baseDirectory); - if (additionalSearchDirectories?.Count > 0) + if (additionalSearchDirectories.Count > 0) { assemblyResolver.AddSearchDirectoriesFromRunSetting(additionalSearchDirectories); return true; diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadSafeStringWriter.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadSafeStringWriter.cs index 8e043fd33c..96127c245b 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadSafeStringWriter.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadSafeStringWriter.cs @@ -117,7 +117,7 @@ protected override void Dispose(bool disposing) { lock (StaticLockObject) { - State?.Value?.Remove(_outputType); + State.Value?.Remove(_outputType); try { base.Dispose(disposing); diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs index 5d5ccc35d9..939eb195a5 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs @@ -104,7 +104,7 @@ internal static string GetTargetFrameworkVersionString(string testSourcePath) CreateInstance( appDomain, assemblyResolverType, - new object[] { resolutionPaths }); + [resolutionPaths]); var assemblyLoadWorker = (AssemblyLoadWorker)CreateInstance( diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AssemblyUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AssemblyUtility.cs index 623b2cec0c..9b32b3e892 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AssemblyUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AssemblyUtility.cs @@ -24,24 +24,24 @@ internal class AssemblyUtility #endif { #if NETFRAMEWORK - private static Dictionary? s_cultures; + private static HashSet? s_cultures; #endif private readonly string[] _assemblyExtensions = [".dll", ".exe"]; #if NETFRAMEWORK /// - /// Gets all supported culture names in Keys. The Values are always null. + /// Gets all supported culture names in Keys. /// - private static Dictionary Cultures + private static HashSet Cultures { get { if (s_cultures == null) { - s_cultures = new Dictionary(StringComparer.OrdinalIgnoreCase); + s_cultures = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (CultureInfo? info in CultureInfo.GetCultures(CultureTypes.AllCultures)) { - s_cultures.Add(info.Name, null); + s_cultures.Add(info.Name); } } @@ -72,17 +72,7 @@ internal class AssemblyUtility /// Path.GetExtension() returns extension with leading dot. /// True if this is an assembly extension. internal bool IsAssemblyExtension(string extensionWithLeadingDot) - { - foreach (string realExtension in _assemblyExtensions) - { - if (string.Equals(extensionWithLeadingDot, realExtension, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } + => _assemblyExtensions.Contains(extensionWithLeadingDot, StringComparer.OrdinalIgnoreCase); #if NETFRAMEWORK /// @@ -136,11 +126,12 @@ internal virtual List GetSatelliteAssemblies(string assemblyPath) } assemblyPath = Path.GetFullPath(assemblyPath); + string assemblyFileName = Path.GetFileName(assemblyPath); string assemblyDir = Path.GetDirectoryName(assemblyPath); var satellites = new List(); // Directory.Exists for 266 dirs takes 9ms while Path.GetDirectories can take up to 80ms on 10k dirs. - foreach (string dir in Cultures.Keys) + foreach (string dir in Cultures) { string dirPath = Path.Combine(assemblyDir, dir); if (!Directory.Exists(dirPath)) @@ -154,7 +145,7 @@ internal virtual List GetSatelliteAssemblies(string assemblyPath) foreach (string extension in _assemblyExtensions) { // extension contains leading dot. - string satellite = Path.ChangeExtension(Path.GetFileName(assemblyPath), "resources" + extension); + string satellite = Path.ChangeExtension(assemblyFileName, "resources" + extension); string satellitePath = Path.Combine(assemblyDir, Path.Combine(dir, satellite)); // We don't use Assembly.LoadFrom/Assembly.GetSatelliteAssemblies because this is rather slow @@ -234,7 +225,7 @@ internal virtual List GetSatelliteAssemblies(string assemblyPath) (AssemblyResolver)AppDomainUtilities.CreateInstance( appDomain, assemblyResolverType, - new object[] { GetResolutionPaths() }); + [GetResolutionPaths()]); // This has to be Load, otherwise Serialization of argument types will not work correctly. var worker = @@ -274,24 +265,15 @@ internal virtual List GetSatelliteAssemblies(string assemblyPath) /// The of resolution paths. internal static IList GetResolutionPaths() { - // Use dictionary to ensure we get a list of unique paths, but keep a list as the - // dictionary does not guarantee order. - Dictionary resolutionPathsDictionary = new(StringComparer.OrdinalIgnoreCase); - List resolutionPaths = []; + string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; - // Add the path of the currently executing assembly (use Uri(CodeBase).LocalPath as Location can be on shadow dir). - string currentlyExecutingAssembly = Path.GetDirectoryName(Path.GetFullPath(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath)); - resolutionPaths.Add(currentlyExecutingAssembly); - resolutionPathsDictionary[currentlyExecutingAssembly] = null; + // Use the path of the currently executing assembly (use Uri(CodeBase).LocalPath as Location can be on shadow dir). + string executingAssembly = Path.GetDirectoryName(Path.GetFullPath(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath))!; // Add the application base for this domain. - if (!resolutionPathsDictionary.ContainsKey(AppDomain.CurrentDomain.BaseDirectory)) - { - resolutionPaths.Add(AppDomain.CurrentDomain.BaseDirectory); - resolutionPathsDictionary[AppDomain.CurrentDomain.BaseDirectory] = null; - } - - return resolutionPaths; + return string.Equals(executingAssembly, baseDirectory, StringComparison.OrdinalIgnoreCase) ? + [executingAssembly] : + [executingAssembly, baseDirectory]; } #endif } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs index 3a88898732..b3408b01b6 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !WINDOWS_UWP @@ -19,6 +19,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Uti /// internal class DeploymentItemUtility { + // REVIEW: it would be better if this was a ReflectionHelper, because helper is able to cache. But we don't have reflection helper here, because this is platform services dll. private readonly ReflectionUtility _reflectionUtility; /// @@ -117,7 +118,7 @@ internal static bool HasDeploymentItems(TestCase testCase) { KeyValuePair[]? deploymentItems = GetDeploymentItems(testCase); - return deploymentItems != null && deploymentItems.Length > 0; + return deploymentItems is { Length: > 0 }; } internal static IList GetDeploymentItems(IEnumerable tests) diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs index 4490f7a44f..8c67e1ad06 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs @@ -417,7 +417,7 @@ private bool Deploy(string source, IRunContext? runContext, ITestExecutionRecord // Log warnings LogWarnings(testExecutionRecorder, warnings); - return deploymentItems != null && deploymentItems.Count > 0; + return deploymentItems is { Count: > 0 }; } private bool IsDeploymentItemSourceAFile(string deploymentItemSourcePath, string testSource, out string file) diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/FileUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/FileUtility.cs index 8cb9e241ac..a31fc260e2 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/FileUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/FileUtility.cs @@ -208,7 +208,7 @@ public virtual List AddFilesFromDirectory(string directoryPath, Func subDirectoryContents = AddFilesFromDirectory(subDirectoryPath, ignoreDirectory, true); - if (subDirectoryContents?.Count > 0) + if (subDirectoryContents.Count > 0) { fileContents.AddRange(subDirectoryContents); } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ReflectionUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ReflectionUtility.cs index 675adad08f..3328729ee0 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ReflectionUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ReflectionUtility.cs @@ -259,8 +259,7 @@ private static void AddNewAttributes( typeof(AttributeUsageAttribute), true); if (attributeUsageAttributes.Count > 0 - && attributeUsageAttributes[0] is AttributeUsageAttribute attributeUsageAttribute - && !attributeUsageAttribute.AllowMultiple) + && attributeUsageAttributes[0] is AttributeUsageAttribute { AllowMultiple: false }) { if (!uniqueAttributes.ContainsKey(attributeInstance.GetType().FullName)) { @@ -291,7 +290,7 @@ private static bool IsTypeInheriting(Type? type1, Type type2) return true; } - type1 = type1.GetTypeInfo().BaseType; + type1 = type1.BaseType; } return false; diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/VSInstallationUtilities.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/VSInstallationUtilities.cs index db902ebbce..292e7c5b45 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/VSInstallationUtilities.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/VSInstallationUtilities.cs @@ -251,9 +251,7 @@ public interface ISetupConfiguration /// [ComImport] [Guid("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D")] - public class SetupConfiguration - { - } + public class SetupConfiguration; } #endif diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/XmlUtilities.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/XmlUtilities.cs index 56e87c2932..4711aed8c8 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/XmlUtilities.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/XmlUtilities.cs @@ -120,7 +120,7 @@ private static void AddAssemblyBindingRedirect( publicKeyTokenString.AppendFormat( System.Globalization.CultureInfo.InvariantCulture, "{0:x2}", - new object[] { publicKeyToken[i] }); + [publicKeyToken[i]]); } } diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/AssemblyCleanupShouldBeValidFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/AssemblyCleanupShouldBeValidFixer.cs new file mode 100644 index 0000000000..352646cef5 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/AssemblyCleanupShouldBeValidFixer.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Composition; + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AssemblyCleanupShouldBeValidFixer))] +[Shared] +public sealed class AssemblyCleanupShouldBeValidFixer : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.AssemblyCleanupShouldBeValidRuleId); + + public override FixAllProvider GetFixAllProvider() + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode node = root.FindNode(context.Span); + if (node == null) + { + return; + } + + if (context.Diagnostics.Any(d => !d.Properties.ContainsKey(DiagnosticDescriptorHelper.CannotFixPropertyKey))) + { + context.RegisterCodeFix( + CodeAction.Create( + CodeFixResources.FixSignatureCodeFix, + ct => FixtureMethodFixer.FixSignatureAsync(context.Document, root, node, isParameterLess: true, shouldBeStatic: true, ct), + nameof(AssemblyCleanupShouldBeValidFixer)), + context.Diagnostics); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/AssemblyInitializeShouldBeValidFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/AssemblyInitializeShouldBeValidFixer.cs new file mode 100644 index 0000000000..b5b0a2965e --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/AssemblyInitializeShouldBeValidFixer.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Composition; + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AssemblyInitializeShouldBeValidFixer))] +[Shared] +public sealed class AssemblyInitializeShouldBeValidFixer : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.AssemblyInitializeShouldBeValidRuleId); + + public override FixAllProvider GetFixAllProvider() + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode node = root.FindNode(context.Span); + if (node == null) + { + return; + } + + if (context.Diagnostics.Any(d => !d.Properties.ContainsKey(DiagnosticDescriptorHelper.CannotFixPropertyKey))) + { + context.RegisterCodeFix( + CodeAction.Create( + CodeFixResources.FixSignatureCodeFix, + ct => FixtureMethodFixer.FixSignatureAsync(context.Document, root, node, isParameterLess: false, shouldBeStatic: true, ct), + nameof(AssemblyInitializeShouldBeValidFixer)), + context.Diagnostics); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldBePassedInCorrectOrderFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldBePassedInCorrectOrderFixer.cs new file mode 100644 index 0000000000..42e8945af4 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldBePassedInCorrectOrderFixer.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Composition; + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AssertionArgsShouldBePassedInCorrectOrderFixer))] +[Shared] +public sealed class AssertionArgsShouldBePassedInCorrectOrderFixer : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.AssertionArgsShouldBePassedInCorrectOrderRuleId); + + public override FixAllProvider GetFixAllProvider() + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + Diagnostic diagnostic = context.Diagnostics[0]; + TextSpan diagnosticSpan = diagnostic.Location.SourceSpan; + + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + if (root.FindNode(diagnosticSpan) is not InvocationExpressionSyntax invocationExpr) + { + return; + } + + if (context.Diagnostics.Any(d => !d.Properties.ContainsKey(DiagnosticDescriptorHelper.CannotFixPropertyKey))) + { + context.RegisterCodeFix( + CodeAction.Create( + CodeFixResources.FixAssertionArgsOrder, + ct => SwapArgumentsAsync(context.Document, root, invocationExpr, ct), + nameof(AssertionArgsShouldBePassedInCorrectOrderFixer)), + context.Diagnostics); + } + } + + private static Task SwapArgumentsAsync(Document document, SyntaxNode root, InvocationExpressionSyntax invocationExpr, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + SeparatedSyntaxList arguments = invocationExpr.ArgumentList.Arguments; + + ArgumentSyntax expectedArg = arguments.FirstOrDefault(arg => IsExpectedArgument(arg)); + ArgumentSyntax actualArg = arguments.FirstOrDefault(arg => IsActualArgument(arg)); + + // Handle positional arguments if named arguments are not found + if (expectedArg == null || actualArg == null) + { + expectedArg = arguments[0]; + actualArg = arguments[1]; + } + + var newArguments = arguments.ToList(); + int expectedIndex = arguments.IndexOf(expectedArg); + int actualIndex = arguments.IndexOf(actualArg); + + newArguments[expectedIndex] = expectedArg.WithExpression(actualArg.Expression); + newArguments[actualIndex] = actualArg.WithExpression(expectedArg.Expression); + + InvocationExpressionSyntax newInvocationExpr = invocationExpr.WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(newArguments))); + SyntaxNode newRoot = root.ReplaceNode(invocationExpr, newInvocationExpr); + + return Task.FromResult(document.WithSyntaxRoot(newRoot)); + } + + private static bool IsActualArgument(ArgumentSyntax argument) => + string.Equals(argument.NameColon?.Name.Identifier.Text, "actual", StringComparison.Ordinal); + + private static bool IsExpectedArgument(ArgumentSyntax argument) => + string.Equals(argument.NameColon?.Name.Identifier.Text, "expected", StringComparison.Ordinal) + || string.Equals(argument.NameColon?.Name.Identifier.Text, "notExpected", StringComparison.Ordinal); +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/ClassCleanupShouldBeValidFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/ClassCleanupShouldBeValidFixer.cs new file mode 100644 index 0000000000..e70adbf288 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/ClassCleanupShouldBeValidFixer.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Composition; + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(ClassCleanupShouldBeValidFixer))] +[Shared] +public sealed class ClassCleanupShouldBeValidFixer : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.ClassCleanupShouldBeValidRuleId); + + public override FixAllProvider GetFixAllProvider() + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode node = root.FindNode(context.Span); + if (node == null) + { + return; + } + + if (context.Diagnostics.Any(d => !d.Properties.ContainsKey(DiagnosticDescriptorHelper.CannotFixPropertyKey))) + { + context.RegisterCodeFix( + CodeAction.Create( + CodeFixResources.FixSignatureCodeFix, + ct => FixtureMethodFixer.FixSignatureAsync(context.Document, root, node, isParameterLess: true, shouldBeStatic: true, ct), + nameof(ClassCleanupShouldBeValidFixer)), + context.Diagnostics); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/ClassInitializeShouldBeValidFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/ClassInitializeShouldBeValidFixer.cs new file mode 100644 index 0000000000..4df53063e2 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/ClassInitializeShouldBeValidFixer.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Composition; + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(ClassInitializeShouldBeValidFixer))] +[Shared] +public sealed class ClassInitializeShouldBeValidFixer : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.ClassInitializeShouldBeValidRuleId); + + public override FixAllProvider GetFixAllProvider() + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode node = root.FindNode(context.Span); + if (node == null) + { + return; + } + + if (context.Diagnostics.Any(d => !d.Properties.ContainsKey(DiagnosticDescriptorHelper.CannotFixPropertyKey))) + { + context.RegisterCodeFix( + CodeAction.Create( + CodeFixResources.FixSignatureCodeFix, + ct => FixtureMethodFixer.FixSignatureAsync(context.Document, root, node, isParameterLess: false, shouldBeStatic: true, ct), + nameof(ClassInitializeShouldBeValidFixer)), + context.Diagnostics); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs index d7c729179c..e52ba64167 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs @@ -1,76 +1,90 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -namespace MSTest.Analyzers; - -using System; -using System.Reflection; - - -/// -/// A strongly-typed resource class, for looking up localized strings, etc. -/// -// This class was auto-generated by the StronglyTypedResourceBuilder -// class via a tool like ResGen or Visual Studio. -// To add or remove a member, edit your .ResX file then rerun ResGen -// with the /str option, or rebuild your VS project. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] -[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] -[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] -internal class CodeFixResources -{ - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal CodeFixResources() - { - } +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +namespace MSTest.Analyzers { + using System; + + /// - /// Returns the cached ResourceManager instance used by this class. + /// A strongly-typed resource class, for looking up localized strings, etc. /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if (object.ReferenceEquals(resourceMan, null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MSTest.Analyzers.CodeFixResources", typeof(CodeFixResources).GetTypeInfo().Assembly); - resourceMan = temp; + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class CodeFixResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CodeFixResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MSTest.Analyzers.CodeFixResources", typeof(CodeFixResources).Assembly); + resourceMan = temp; + } + return resourceMan; } - return resourceMan; } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } } - set - { - resourceCulture = value; + + /// + /// Looks up a localized string similar to Fix signature. + /// + internal static string AssemblyInitializeShouldBeValidCodeFix { + get { + return ResourceManager.GetString("AssemblyInitializeShouldBeValidCodeFix", resourceCulture); + } } - } - - /// - /// Looks up a localized string similar to Make uppercase. - /// - internal static string CodeFixTitle - { - get - { - return ResourceManager.GetString("CodeFixTitle", resourceCulture); + + /// + /// Looks up a localized string similar to Fix actual/expected arguments order. + /// + internal static string FixAssertionArgsOrder { + get { + return ResourceManager.GetString("FixAssertionArgsOrder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fix signature. + /// + internal static string FixSignatureCodeFix { + get { + return ResourceManager.GetString("FixSignatureCodeFix", resourceCulture); + } } } } diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx index 97abe68945..227cf36858 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx @@ -117,8 +117,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Make uppercase - The title of the code fix. + + Fix signature + + + Fix signature + + + Fix actual/expected arguments order \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/Helpers/FixtureMethodFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/Helpers/FixtureMethodFixer.cs new file mode 100644 index 0000000000..f4effcbe6c --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/Helpers/FixtureMethodFixer.cs @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Editing; + +namespace MSTest.Analyzers.Helpers; + +internal static class FixtureMethodFixer +{ + private const SyntaxNode? VoidReturnTypeNode = null; + + public static async Task FixSignatureAsync(Document document, SyntaxNode root, SyntaxNode node, + bool isParameterLess, bool shouldBeStatic, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + if (semanticModel is null) + { + return document.Project.Solution; + } + + var methodSymbol = (IMethodSymbol?)semanticModel.GetDeclaredSymbol(node, cancellationToken); + if (methodSymbol is null) + { + return document.Project.Solution; + } + + var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(semanticModel.Compilation); + var syntaxGenerator = SyntaxGenerator.GetGenerator(document); + + SyntaxNode fixedMethodDeclarationNode = syntaxGenerator.MethodDeclaration( + methodSymbol.Name, + GetParameters(syntaxGenerator, isParameterLess, wellKnownTypeProvider), + typeParameters: null, + GetReturnType(syntaxGenerator, methodSymbol, wellKnownTypeProvider), + Accessibility.Public, + GetModifiers(methodSymbol, shouldBeStatic), + GetStatements(node, syntaxGenerator)); + + // Copy the attributes from the old method to the new method. + fixedMethodDeclarationNode = syntaxGenerator.AddAttributes(fixedMethodDeclarationNode, syntaxGenerator.GetAttributes(node)); + + return document.WithSyntaxRoot(root.ReplaceNode(node, fixedMethodDeclarationNode)).Project.Solution; + } + + private static IEnumerable GetStatements(SyntaxNode node, SyntaxGenerator syntaxGenerator) + => syntaxGenerator.GetStatements(node) + .Where(x => !x.IsKind(SyntaxKind.ReturnStatement) && !x.IsKind(SyntaxKind.YieldReturnStatement)); + + private static DeclarationModifiers GetModifiers(IMethodSymbol methodSymbol, bool shouldBeStatic) + { + DeclarationModifiers newModifiers = methodSymbol.IsAsync + ? DeclarationModifiers.Async + : DeclarationModifiers.None; + + return newModifiers.WithIsStatic(shouldBeStatic); + } + + private static SyntaxNode? GetReturnType(SyntaxGenerator syntaxGenerator, IMethodSymbol methodSymbol, WellKnownTypeProvider wellKnownTypeProvider) + { + if (SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType.OriginalDefinition, wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask1))) + { + return syntaxGenerator.IdentifierName("ValueTask"); + } + + if (methodSymbol.IsAsync + || SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType.OriginalDefinition, wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask1))) + { + return syntaxGenerator.IdentifierName("Task"); + } + + // For all other cases return void. + return VoidReturnTypeNode; + } + + private static IEnumerable GetParameters(SyntaxGenerator syntaxGenerator, bool isParameterLess, + WellKnownTypeProvider wellKnownTypeProvider) + { + if (isParameterLess + || !wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName( + WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestContext, + out INamedTypeSymbol? testContextTypeSymbol)) + { + return []; + } + + SyntaxNode testContextType = syntaxGenerator.TypeExpression(testContextTypeSymbol); + SyntaxNode testContextParameter = syntaxGenerator.ParameterDeclaration("testContext", testContextType); + return [testContextParameter]; + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/MSTest.Analyzers.CodeFixes.csproj b/src/Analyzers/MSTest.Analyzers.CodeFixes/MSTest.Analyzers.CodeFixes.csproj index 840b0c9e95..90c2706c8f 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/MSTest.Analyzers.CodeFixes.csproj +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/MSTest.Analyzers.CodeFixes.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -11,7 +11,7 @@ - + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/RoslynAnalyzerHelpers/.editorconfig b/src/Analyzers/MSTest.Analyzers.CodeFixes/RoslynAnalyzerHelpers/.editorconfig new file mode 100644 index 0000000000..89b66eaea7 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/RoslynAnalyzerHelpers/.editorconfig @@ -0,0 +1,36 @@ +root = false + +[*.cs] + +dotnet_diagnostic.IDE0003.severity = none +dotnet_diagnostic.IDE0005.severity = none +dotnet_diagnostic.IDE0008.severity = none +dotnet_diagnostic.IDE0011.severity = none +dotnet_diagnostic.IDE0045.severity = none +dotnet_diagnostic.IDE0046.severity = none +dotnet_diagnostic.IDE0073.severity = none +dotnet_diagnostic.IDE0161.severity = none +dotnet_diagnostic.IDE1006.severity = none + +dotnet_diagnostic.SA1025.severity = none +dotnet_diagnostic.SA1028.severity = none +dotnet_diagnostic.SA1108.severity = none +dotnet_diagnostic.SA1116.severity = none +dotnet_diagnostic.SA1127.severity = none +dotnet_diagnostic.SA1201.severity = none +dotnet_diagnostic.SA1214.severity = none +dotnet_diagnostic.SA1311.severity = none +dotnet_diagnostic.SA1314.severity = none +dotnet_diagnostic.SA1405.severity = none +dotnet_diagnostic.SA1413.severity = none +dotnet_diagnostic.SA1502.severity = none +dotnet_diagnostic.SA1503.severity = none +dotnet_diagnostic.SA1512.severity = none +dotnet_diagnostic.SA1516.severity = none +dotnet_diagnostic.SA1602.severity = none +dotnet_diagnostic.SA1604.severity = none +dotnet_diagnostic.SA1618.severity = none +dotnet_diagnostic.SA1629.severity = none +dotnet_diagnostic.SA1636.severity = none + +dotnet_diagnostic.RS1035.severity = none diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/RoslynAnalyzerHelpers/DocumentExtensions.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/RoslynAnalyzerHelpers/DocumentExtensions.cs new file mode 100644 index 0000000000..e85c187589 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/RoslynAnalyzerHelpers/DocumentExtensions.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +#if !MICROSOFT_CODEANALYSIS_PUBLIC_API_ANALYZERS + +using System; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities +{ + internal static class DocumentExtensions + { + public static async ValueTask GetRequiredSemanticModelAsync(this Document document, CancellationToken cancellationToken) + { + if (document.TryGetSemanticModel(out var semanticModel)) + return semanticModel; + + semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + return semanticModel ?? throw new InvalidOperationException("SyntaxTree is required to accomplish the task but is not supported by document"); + } + + public static async ValueTask GetRequiredSyntaxTreeAsync(this Document document, CancellationToken cancellationToken) + { + if (document.TryGetSyntaxTree(out var syntaxTree)) + return syntaxTree; + + syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + return syntaxTree ?? throw new InvalidOperationException("SyntaxTree is required to accomplish the task but is not supported by document"); + } + + public static async ValueTask GetRequiredSyntaxRootAsync(this Document document, CancellationToken cancellationToken) + { + if (document.TryGetSyntaxRoot(out var root)) + return root; + + root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + return root ?? throw new InvalidOperationException("SyntaxTree is required to accomplish the task but is not supported by document"); + } + } +} + +#endif diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/TestCleanupShouldBeValidFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/TestCleanupShouldBeValidFixer.cs new file mode 100644 index 0000000000..bad2decb21 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/TestCleanupShouldBeValidFixer.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Composition; + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(TestCleanupShouldBeValidFixer))] +[Shared] +public sealed class TestCleanupShouldBeValidFixer : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.TestCleanupShouldBeValidRuleId); + + public override FixAllProvider GetFixAllProvider() + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode node = root.FindNode(context.Span); + if (node == null) + { + return; + } + + if (context.Diagnostics.Any(d => !d.Properties.ContainsKey(DiagnosticDescriptorHelper.CannotFixPropertyKey))) + { + context.RegisterCodeFix( + CodeAction.Create( + CodeFixResources.FixSignatureCodeFix, + ct => FixtureMethodFixer.FixSignatureAsync(context.Document, root, node, isParameterLess: true, shouldBeStatic: false, ct), + nameof(TestCleanupShouldBeValidFixer)), + context.Diagnostics); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/TestInitializeShouldBeValidFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/TestInitializeShouldBeValidFixer.cs new file mode 100644 index 0000000000..dec0fe9cee --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/TestInitializeShouldBeValidFixer.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Composition; + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(TestInitializeShouldBeValidFixer))] +[Shared] +public sealed class TestInitializeShouldBeValidFixer : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.TestInitializeShouldBeValidRuleId); + + public override FixAllProvider GetFixAllProvider() + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode node = root.FindNode(context.Span); + if (node == null) + { + return; + } + + if (context.Diagnostics.Any(d => !d.Properties.ContainsKey(DiagnosticDescriptorHelper.CannotFixPropertyKey))) + { + context.RegisterCodeFix( + CodeAction.Create( + CodeFixResources.FixSignatureCodeFix, + ct => FixtureMethodFixer.FixSignatureAsync(context.Document, root, node, isParameterLess: true, shouldBeStatic: false, ct), + nameof(TestInitializeShouldBeValidFixer)), + context.Diagnostics); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseParallelizeAttributeFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseParallelizeAttributeFixer.cs deleted file mode 100644 index 09881605ea..0000000000 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseParallelizeAttributeFixer.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections.Immutable; -using System.Composition; - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; - -namespace MSTest.Analyzers; - -[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseParallelizeAttributeFixer))] -[Shared] -public sealed class UseParallelizeAttributeFixer : CodeFixProvider -{ - public sealed override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Empty; - - public override FixAllProvider GetFixAllProvider() => - // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers - WellKnownFixAllProviders.BatchFixer; - - public override Task RegisterCodeFixesAsync(CodeFixContext context) => - // Fixer not yet implemented. - Task.CompletedTask; -} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf index d7844f6b33..4c7b6c42df 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf @@ -2,10 +2,20 @@ - - Make uppercase - Převést na velká písmena - The title of the code fix. + + Fix actual/expected arguments order + Opravit pořadí skutečných/očekávaných argumentů + + + + Fix signature + Opravit podpis + + + + Fix signature + Opravit podpis + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf index 895ba0a996..cfbe1881b8 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf @@ -2,10 +2,20 @@ - - Make uppercase - In Großbuchstaben umwandeln - The title of the code fix. + + Fix actual/expected arguments order + Reihenfolge der tatsächlichen/erwarteten Argumente korrigieren + + + + Fix signature + Signatur korrigieren + + + + Fix signature + Signatur korrigieren + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf index 44ee0ef015..267f7a3ff9 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf @@ -2,10 +2,20 @@ - - Make uppercase - Poner en mayúsculas - The title of the code fix. + + Fix actual/expected arguments order + Corregir el orden de los argumentos reales o esperados + + + + Fix signature + Corregir firma + + + + Fix signature + Corregir firma + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf index 2edae75093..7ff9b41620 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf @@ -2,10 +2,20 @@ - - Make uppercase - Mettre en majuscules - The title of the code fix. + + Fix actual/expected arguments order + Corriger l’ordre des arguments réels/attendus + + + + Fix signature + Corriger la signature numérique + + + + Fix signature + Corriger la signature numérique + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf index d5cdfbbf88..60c8f6be20 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf @@ -2,10 +2,20 @@ - - Make uppercase - Rendi maiuscola - The title of the code fix. + + Fix actual/expected arguments order + Correggi l'ordine degli argomenti effettivi/previsti + + + + Fix signature + Correggi firma + + + + Fix signature + Correggi firma + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf index 0f8364dbc2..3712abe1d2 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf @@ -2,10 +2,20 @@ - - Make uppercase - 大文字に変換 - The title of the code fix. + + Fix actual/expected arguments order + 実際の引数と予想される引数の順序を修正する + + + + Fix signature + 署名の修正 + + + + Fix signature + 署名の修正 + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf index e062c13e77..ec3b6d4afe 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf @@ -2,10 +2,20 @@ - - Make uppercase - 대문자 만들기 - The title of the code fix. + + Fix actual/expected arguments order + 실제/예상 인수 순서 수정 + + + + Fix signature + 서명 수정 + + + + Fix signature + 서명 수정 + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf index 5886202787..79426e9b66 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf @@ -2,10 +2,20 @@ - - Make uppercase - Zmiana na wielkie litery - The title of the code fix. + + Fix actual/expected arguments order + Napraw rzeczywistą/oczekiwaną kolejność argumentów + + + + Fix signature + Popraw podpis + + + + Fix signature + Popraw podpis + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf index af006c5f64..0d39f1845c 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf @@ -2,10 +2,20 @@ - - Make uppercase - Tornar maiúsculas - The title of the code fix. + + Fix actual/expected arguments order + Corrigir ordem de argumentos real/esperada + + + + Fix signature + Corrigir assinatura + + + + Fix signature + Corrigir assinatura + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf index a6ac452e67..2a7f059394 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf @@ -2,10 +2,20 @@ - - Make uppercase - Сделать прописными - The title of the code fix. + + Fix actual/expected arguments order + Исправить порядок фактических и ожидаемых аргументов + + + + Fix signature + Исправить подпись + + + + Fix signature + Исправить подпись + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf index a4ed406fa9..379fd8b09e 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf @@ -2,10 +2,20 @@ - - Make uppercase - Büyük harfe dönüştür - The title of the code fix. + + Fix actual/expected arguments order + Fiili/beklenen bağımsız değişken sırasını düzelt + + + + Fix signature + İmzayı düzelt + + + + Fix signature + İmzayı düzelt + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf index 9b59710b58..6109110c83 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf @@ -2,10 +2,20 @@ - - Make uppercase - 转换为大写 - The title of the code fix. + + Fix actual/expected arguments order + 修复实际/预期参数顺序 + + + + Fix signature + 修复签名 + + + + Fix signature + 修复签名 + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf index c0121ba11a..cb1527e569 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf @@ -2,10 +2,20 @@ - - Make uppercase - 設成大寫 - The title of the code fix. + + Fix actual/expected arguments order + 修正實際/預期的引數順序 + + + + Fix signature + 修正簽章 + + + + Fix signature + 修正簽章 + diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md index a253cdd0b6..efd829aa0c 100644 --- a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md +++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md @@ -1,4 +1,19 @@ -## Release 3.3.0 +## Release 3.4.0 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +MSTEST0017 | Usage | Info | AssertionArgsShouldBePassedInCorrectOrder, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0017) +MSTEST0019 | Design | Disabled | PreferTestInitializeOverConstructorAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0019) +MSTEST0020 | Design | Disabled | PreferConstructorOverTestInitializeAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0020) +MSTEST0021 | Design | Disabled | PreferDisposeOverTestCleanupAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0021) +MSTEST0022 | Design | Disabled | PreferTestCleanupOverDisposeAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0022) +MSTEST0023 | Usage | Info | DoNotNegateBooleanAssertionAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0023) +MSTEST0024 | Usage | Info | DoNotStoreStaticTestContextAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0024) +MSTEST0025 | Usage | Info | PreferAssertFailOverAlwaysFalseConditionsAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0025) + +## Release 3.3.0 ### New Rules diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md index 6f9452a4df..18a88eed2e 100644 --- a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md @@ -1,14 +1,12 @@ ; Unshipped analyzer release ; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + ### New Rules Rule ID | Category | Severity | Notes --------|----------|----------|------- -MSTEST0017 | Usage | Info | AssertionArgsShouldBePassedInCorrectOrder, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0017) -MSTEST0019 | Design | Disabled | PreferTestInitializeOverConstructorAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0019) -MSTEST0020 | Design | Disabled | PreferConstructorOverTestInitializeAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0020) -MSTEST0021 | Design | Disabled | PreferDisposeOverTestCleanupAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0021) -MSTEST0022 | Design | Disabled | PreferTestCleanupOverDisposeAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0022) -MSTEST0023 | Usage | Info | DoNotNegateBooleanAssertionAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0023) -MSTEST0024 | Usage | Info | DoNotStoreStaticTestContextAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0024) -MSTEST0025 | Usage | Info | PreferAssertFailOverAlwaysFalseConditionsAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0025) +MSTEST0026 | Usage | Info | AssertionArgsShouldAvoidConditionalAccessRuleId, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0026) +MSTEST0029 | Design | Disabled | PublicMethodShouldBeTestMethodAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0029) +MSTEST0030 | Usage | Info | TypeContainingTestMethodShouldBeATestClassAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0030) +MSTEST0031 | Usage | Info | DoNotUseSystemDescriptionAttributeAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0031) +MSTEST0032 | Usage | Info | ReviewAlwaysTrueAssertConditionAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0032) diff --git a/src/Analyzers/MSTest.Analyzers/AssemblyCleanupShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AssemblyCleanupShouldBeValidAnalyzer.cs index 6073570a56..df919b1b36 100644 --- a/src/Analyzers/MSTest.Analyzers/AssemblyCleanupShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/AssemblyCleanupShouldBeValidAnalyzer.cs @@ -12,32 +12,22 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0013: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AssemblyCleanupShouldBeValidAnalyzer : DiagnosticAnalyzer { - private static readonly LocalizableResourceString Title = new(nameof(Resources.AssemblyCleanupShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableResourceString Description = new(nameof(Resources.AssemblyCleanupShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.AssemblyCleanupShouldBeValidMessageFormat_Public), Resources.ResourceManager, typeof(Resources)); - - internal static readonly DiagnosticDescriptor PublicRule = DiagnosticDescriptorHelper.Create( + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( DiagnosticIds.AssemblyCleanupShouldBeValidRuleId, - Title, - MessageFormat, - Description, + new LocalizableResourceString(nameof(Resources.AssemblyCleanupShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.AssemblyCleanupShouldBeValidMessageFormat), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.AssemblyCleanupShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)), Category.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true); - internal static readonly DiagnosticDescriptor StaticRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyCleanupShouldBeValidMessageFormat_Static), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NoParametersRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyCleanupShouldBeValidMessageFormat_NoParameters), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor ReturnTypeRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyCleanupShouldBeValidMessageFormat_ReturnType), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotAsyncVoidRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyCleanupShouldBeValidMessageFormat_NotAsyncVoid), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotGenericRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyCleanupShouldBeValidMessageFormat_NotGeneric), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor OrdinaryRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyCleanupShouldBeValidMessageFormat_Ordinary), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotAGenericClassRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyCleanupShouldBeValidMessageFormat_NotAGenericClass), Resources.ResourceManager, typeof(Resources))); - - public override ImmutableArray SupportedDiagnostics { get; } - = ImmutableArray.Create(PublicRule); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { @@ -46,20 +36,20 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { - if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssemblyCleanupAttribute, out INamedTypeSymbol? assemblyCleanupAttributeSymbol)) + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssemblyCleanupAttribute, out INamedTypeSymbol? assemblyCleanupAttributeSymbol) + && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute, out INamedTypeSymbol? testClassAttributeSymbol)) { INamedTypeSymbol? taskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); INamedTypeSymbol? valueTaskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); bool canDiscoverInternals = context.Compilation.CanDiscoverInternals(); context.RegisterSymbolAction( - context => AnalyzeSymbol(context, assemblyCleanupAttributeSymbol, taskSymbol, valueTaskSymbol, canDiscoverInternals), + context => AnalyzeSymbol(context, assemblyCleanupAttributeSymbol, testClassAttributeSymbol, taskSymbol, valueTaskSymbol, canDiscoverInternals), SymbolKind.Method); } }); } - private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol assemblyCleanupAttributeSymbol, INamedTypeSymbol? taskSymbol, - INamedTypeSymbol? valueTaskSymbol, bool canDiscoverInternals) + private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol assemblyCleanupAttributeSymbol, INamedTypeSymbol testClassAttributeSymbol, INamedTypeSymbol? taskSymbol, INamedTypeSymbol? valueTaskSymbol, bool canDiscoverInternals) { var methodSymbol = (IMethodSymbol)context.Symbol; @@ -68,49 +58,11 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo return; } - if (methodSymbol.MethodKind != MethodKind.Ordinary) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(OrdinaryRule, methodSymbol.Name)); - - // Do not check the other criteria, users should fix the method kind first. - return; - } - - if (context.Symbol.ContainingType.IsGenericType) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAGenericClassRule, methodSymbol.Name)); - } - - if (methodSymbol.Parameters.Length > 0) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NoParametersRule, methodSymbol.Name)); - } - - if (methodSymbol.IsGenericMethod) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotGenericRule, methodSymbol.Name)); - } - - if (!methodSymbol.IsStatic) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(StaticRule, methodSymbol.Name)); - } - - if (methodSymbol.ReturnsVoid && methodSymbol.IsAsync) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAsyncVoidRule, methodSymbol.Name)); - } - - if (!methodSymbol.IsPublicAndHasCorrectResultantVisibility(canDiscoverInternals)) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(PublicRule, methodSymbol.Name)); - } - - if (!methodSymbol.ReturnsVoid - && (taskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, taskSymbol)) - && (valueTaskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, valueTaskSymbol))) + if (!methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: true, allowGenericType: false, testContextSymbol: null, testClassAttributeSymbol, fixtureAllowInheritedTestClass: false, out bool isFixable)) { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(ReturnTypeRule, methodSymbol.Name)); + context.ReportDiagnostic(isFixable + ? methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name) + : methodSymbol.CreateDiagnostic(Rule, DiagnosticDescriptorHelper.CannotFixProperties, methodSymbol.Name)); } } } diff --git a/src/Analyzers/MSTest.Analyzers/AssemblyInitializeShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AssemblyInitializeShouldBeValidAnalyzer.cs index b0f2e90a05..b547b9b71a 100644 --- a/src/Analyzers/MSTest.Analyzers/AssemblyInitializeShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/AssemblyInitializeShouldBeValidAnalyzer.cs @@ -12,32 +12,22 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0012: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AssemblyInitializeShouldBeValidAnalyzer : DiagnosticAnalyzer { - private static readonly LocalizableResourceString Title = new(nameof(Resources.AssemblyInitializeShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableResourceString Description = new(nameof(Resources.AssemblyInitializeShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.AssemblyInitializeShouldBeValidMessageFormat_Public), Resources.ResourceManager, typeof(Resources)); - - internal static readonly DiagnosticDescriptor PublicRule = DiagnosticDescriptorHelper.Create( + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( DiagnosticIds.AssemblyInitializeShouldBeValidRuleId, - Title, - MessageFormat, - Description, + new LocalizableResourceString(nameof(Resources.AssemblyInitializeShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.AssemblyInitializeShouldBeValidMessageFormat), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.AssemblyInitializeShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)), Category.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true); - internal static readonly DiagnosticDescriptor StaticRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyInitializeShouldBeValidMessageFormat_Static), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor SingleContextParameterRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyInitializeShouldBeValidMessageFormat_SingleContextParameter), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor ReturnTypeRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyInitializeShouldBeValidMessageFormat_ReturnType), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotAsyncVoidRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyInitializeShouldBeValidMessageFormat_NotAsyncVoid), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotGenericRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyInitializeShouldBeValidMessageFormat_NotGeneric), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor OrdinaryRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyInitializeShouldBeValidMessageFormat_Ordinary), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotAGenericClassRule = PublicRule.WithMessage(new(nameof(Resources.AssemblyInitializeShouldBeValidMessageFormat_NotAGenericClass), Resources.ResourceManager, typeof(Resources))); - - public override ImmutableArray SupportedDiagnostics { get; } - = ImmutableArray.Create(PublicRule); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { @@ -47,72 +37,31 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssemblyInitializeAttribute, out INamedTypeSymbol? assemblyInitializeAttributeSymbol) - && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestContext, out INamedTypeSymbol? testContextSymbol)) + && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestContext, out INamedTypeSymbol? testContextSymbol) + && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute, out INamedTypeSymbol? testClassAttributeSymbol)) { INamedTypeSymbol? taskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); INamedTypeSymbol? valueTaskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); bool canDiscoverInternals = context.Compilation.CanDiscoverInternals(); context.RegisterSymbolAction( - context => AnalyzeSymbol(context, assemblyInitializeAttributeSymbol, taskSymbol, valueTaskSymbol, testContextSymbol, canDiscoverInternals), + context => AnalyzeSymbol(context, assemblyInitializeAttributeSymbol, taskSymbol, valueTaskSymbol, testContextSymbol, testClassAttributeSymbol, canDiscoverInternals), SymbolKind.Method); } }); } private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol assemblyInitializeAttributeSymbol, INamedTypeSymbol? taskSymbol, - INamedTypeSymbol? valueTaskSymbol, INamedTypeSymbol? testContextSymbol, bool canDiscoverInternals) + INamedTypeSymbol? valueTaskSymbol, INamedTypeSymbol testContextSymbol, INamedTypeSymbol testClassAttributeSymbol, bool canDiscoverInternals) { var methodSymbol = (IMethodSymbol)context.Symbol; - if (!methodSymbol.IsAssemblyInitializeMethod(assemblyInitializeAttributeSymbol)) - { - return; - } - - if (methodSymbol.MethodKind != MethodKind.Ordinary) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(OrdinaryRule, methodSymbol.Name)); - - // Do not check the other criteria, users should fix the method kind first. - return; - } - - if (context.Symbol.ContainingType.IsGenericType) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAGenericClassRule, methodSymbol.Name)); - } - - if (methodSymbol.Parameters.Length != 1 || testContextSymbol is null || - !SymbolEqualityComparer.Default.Equals(methodSymbol.Parameters[0].Type, testContextSymbol)) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(SingleContextParameterRule, methodSymbol.Name)); - } - - if (methodSymbol.IsGenericMethod) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotGenericRule, methodSymbol.Name)); - } - - if (!methodSymbol.IsStatic) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(StaticRule, methodSymbol.Name)); - } - - if (methodSymbol.ReturnsVoid && methodSymbol.IsAsync) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAsyncVoidRule, methodSymbol.Name)); - } - - if (!methodSymbol.IsPublicAndHasCorrectResultantVisibility(canDiscoverInternals)) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(PublicRule, methodSymbol.Name)); - } - - if (!methodSymbol.ReturnsVoid - && (taskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, taskSymbol)) - && (valueTaskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, valueTaskSymbol))) + if (methodSymbol.IsAssemblyInitializeMethod(assemblyInitializeAttributeSymbol) + && !methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: true, + allowGenericType: false, testContextSymbol, testClassAttributeSymbol, fixtureAllowInheritedTestClass: false, out bool isFixable)) { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(ReturnTypeRule, methodSymbol.Name)); + context.ReportDiagnostic(isFixable + ? methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name) + : methodSymbol.CreateDiagnostic(Rule, DiagnosticDescriptorHelper.CannotFixProperties, methodSymbol.Name)); } } } diff --git a/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldAvoidConditionalAccessAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldAvoidConditionalAccessAnalyzer.cs new file mode 100644 index 0000000000..6cfe536e07 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldAvoidConditionalAccessAnalyzer.cs @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0026: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class AssertionArgsShouldAvoidConditionalAccessAnalyzer : DiagnosticAnalyzer +{ + private static readonly ImmutableArray AssertSupportedMethodNames = ImmutableArray.Create([ + "IsTrue", + "IsFalse", + "AreEqual", + "AreNotEqual", + "AreSame", + "AreNotSame" + ]); + + private static readonly ImmutableArray CollectionAssertSupportedMethodNames = ImmutableArray.Create([ + "IsTrue", + "IsFalse", + "AreEqual", + "AreNotEqual", + "AreEquivalent", + "AreNotEquivalent", + "Contains", + "DoesNotContain", + "AllItemsAreNotNull", + "AllItemsAreUnique", + "IsSubsetOf", + "IsNotSubsetOf", + "AllItemsAreInstancesOfType" + ]); + + private static readonly ImmutableArray StringAssertSupportedMethodNames = ImmutableArray.Create([ + "Contains", + "StartsWith", + "EndsWith", + "Matches", + "DoesNotMatch" + ]); + + private static readonly LocalizableResourceString Title = new(nameof(Resources.AssertionArgsShouldAvoidConditionalAccessTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.AssertionArgsShouldAvoidConditionalAccessMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.AssertionArgsShouldAvoidConditionalAccessRuleId, + Title, + MessageFormat, + description: null, + Category.Usage, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssert, out INamedTypeSymbol? assertSymbol)) + { + context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol, AssertSupportedMethodNames), OperationKind.Invocation); + } + + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingCollectionAssert, out INamedTypeSymbol? collectionAssertSymbol)) + { + context.RegisterOperationAction(context => AnalyzeOperation(context, collectionAssertSymbol, CollectionAssertSupportedMethodNames), OperationKind.Invocation); + } + + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingStringAssert, out INamedTypeSymbol? stringAssertSymbol)) + { + context.RegisterOperationAction(context => AnalyzeOperation(context, stringAssertSymbol, StringAssertSupportedMethodNames), OperationKind.Invocation); + } + }); + } + + private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol, ImmutableArray supportedMethodNames) + { + var invocationOperation = (IInvocationOperation)context.Operation; + + // This is not an invocation of the expected assertion methods. + if (!supportedMethodNames.Contains(invocationOperation.TargetMethod.Name) + || !SymbolEqualityComparer.Default.Equals(assertSymbol, invocationOperation.TargetMethod.ContainingType) + || !HasAnyConditionalAccessOperationChild(invocationOperation)) + { + return; + } + + context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule)); + } + + private static bool HasAnyConditionalAccessOperationChild(IInvocationOperation invocationOperation) + { + foreach (IArgumentOperation argument in invocationOperation.Arguments) + { + // Check for conditional access + // a?.b + // a?.b?.c + // a.b?.c + if (argument.Value is IConditionalAccessOperation { Kind: OperationKind.ConditionalAccess }) + { + return true; + } + + // Check for binary operations with conditional access => s?.Length > 1. + if (argument.Value is IBinaryOperation binaryOperation) + { + if (binaryOperation.LeftOperand.Kind == OperationKind.ConditionalAccess || binaryOperation.RightOperand.Kind == OperationKind.ConditionalAccess) + { + return true; + } + } + + // Check for conversion operations with conditional access => (s?.Length). + if (argument.Value is IConversionOperation { Operand.Kind: OperationKind.ConditionalAccess }) + { + return true; + } + } + + return false; + } +} diff --git a/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldBePassedInCorrectOrderAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldBePassedInCorrectOrderAnalyzer.cs index f4ea7a905a..a675d0fc33 100644 --- a/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldBePassedInCorrectOrderAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldBePassedInCorrectOrderAnalyzer.cs @@ -14,16 +14,18 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0017: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AssertionArgsShouldBePassedInCorrectOrderAnalyzer : DiagnosticAnalyzer { - private static readonly ImmutableArray SupportedMethodNames = ImmutableArray.Create(new[] - { + private static readonly ImmutableArray SupportedMethodNames = ImmutableArray.Create([ "AreEqual", "AreNotEqual", "AreSame", - "AreNotSame", - }); + "AreNotSame" + ]); private static readonly LocalizableResourceString Title = new(nameof(Resources.AssertionArgsShouldBePassedInCorrectOrderTitle), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableResourceString Description = new(nameof(Resources.AssertionArgsShouldBePassedInCorrectOrderDescription), Resources.ResourceManager, typeof(Resources)); diff --git a/src/Analyzers/MSTest.Analyzers/AvoidExpectedExceptionAttributeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AvoidExpectedExceptionAttributeAnalyzer.cs index f9a30f6644..bf99f5e79f 100644 --- a/src/Analyzers/MSTest.Analyzers/AvoidExpectedExceptionAttributeAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/AvoidExpectedExceptionAttributeAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0006: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AvoidExpectedExceptionAttributeAnalyzer : DiagnosticAnalyzer { diff --git a/src/Analyzers/MSTest.Analyzers/ClassCleanupShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/ClassCleanupShouldBeValidAnalyzer.cs index b21d47b333..bbee86ef59 100644 --- a/src/Analyzers/MSTest.Analyzers/ClassCleanupShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/ClassCleanupShouldBeValidAnalyzer.cs @@ -3,7 +3,6 @@ using System.Collections.Immutable; -using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; @@ -13,32 +12,22 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0011: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class ClassCleanupShouldBeValidAnalyzer : DiagnosticAnalyzer { - private static readonly LocalizableResourceString Title = new(nameof(Resources.ClassCleanupShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableResourceString Description = new(nameof(Resources.ClassCleanupShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.ClassCleanupShouldBeValidMessageFormat_Public), Resources.ResourceManager, typeof(Resources)); - - internal static readonly DiagnosticDescriptor PublicRule = DiagnosticDescriptorHelper.Create( + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( DiagnosticIds.ClassCleanupShouldBeValidRuleId, - Title, - MessageFormat, - Description, + new LocalizableResourceString(nameof(Resources.ClassCleanupShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.ClassCleanupShouldBeValidMessageFormat), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.ClassCleanupShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)), Category.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true); - internal static readonly DiagnosticDescriptor StaticRule = PublicRule.WithMessage(new(nameof(Resources.ClassCleanupShouldBeValidMessageFormat_Static), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NoParametersRule = PublicRule.WithMessage(new(nameof(Resources.ClassCleanupShouldBeValidMessageFormat_NoParameters), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor ReturnTypeRule = PublicRule.WithMessage(new(nameof(Resources.ClassCleanupShouldBeValidMessageFormat_ReturnType), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotAsyncVoidRule = PublicRule.WithMessage(new(nameof(Resources.ClassCleanupShouldBeValidMessageFormat_NotAsyncVoid), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotGenericRule = PublicRule.WithMessage(new(nameof(Resources.ClassCleanupShouldBeValidMessageFormat_NotGeneric), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor OrdinaryRule = PublicRule.WithMessage(new(nameof(Resources.ClassCleanupShouldBeValidMessageFormat_Ordinary), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotAGenericClassUnlessInheritanceModeSetRule = PublicRule.WithMessage(new(nameof(Resources.ClassCleanupShouldBeValidMessageFormat_NotAGenericClassUnlessInheritanceModeSet), Resources.ResourceManager, typeof(Resources))); - - public override ImmutableArray SupportedDiagnostics { get; } - = ImmutableArray.Create(PublicRule); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { @@ -47,103 +36,33 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { - if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassCleanupAttribute, out INamedTypeSymbol? classCleanupAttributeSymbol)) + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassCleanupAttribute, out INamedTypeSymbol? classCleanupAttributeSymbol) + && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute, out INamedTypeSymbol? testClassAttributeSymbol)) { INamedTypeSymbol? taskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); INamedTypeSymbol? inheritanceBehaviorSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingInheritanceBehavior); INamedTypeSymbol? valueTaskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); bool canDiscoverInternals = context.Compilation.CanDiscoverInternals(); context.RegisterSymbolAction( - context => AnalyzeSymbol(context, classCleanupAttributeSymbol, taskSymbol, valueTaskSymbol, inheritanceBehaviorSymbol, canDiscoverInternals), + context => AnalyzeSymbol(context, classCleanupAttributeSymbol, taskSymbol, valueTaskSymbol, inheritanceBehaviorSymbol, testClassAttributeSymbol, canDiscoverInternals), SymbolKind.Method); } }); } private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol classCleanupAttributeSymbol, INamedTypeSymbol? taskSymbol, - INamedTypeSymbol? valueTaskSymbol, INamedTypeSymbol? inheritanceBehaviorSymbol, bool canDiscoverInternals) + INamedTypeSymbol? valueTaskSymbol, INamedTypeSymbol? inheritanceBehaviorSymbol, INamedTypeSymbol testClassAttributeSymbol, bool canDiscoverInternals) { var methodSymbol = (IMethodSymbol)context.Symbol; - if (!methodSymbol.IsClassCleanupMethod(classCleanupAttributeSymbol)) - { - return; - } - - if (methodSymbol.MethodKind != MethodKind.Ordinary) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(OrdinaryRule, methodSymbol.Name)); - - // Do not check the other criteria, users should fix the method kind first. - return; - } - - if (context.Symbol.ContainingType.IsGenericType) - { - bool isInheritanceModeSet = false; - foreach (AttributeData attr in methodSymbol.GetAttributes()) - { - if (!SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classCleanupAttributeSymbol)) - { - continue; - } - - ImmutableArray constructorArguments = attr.ConstructorArguments; - foreach (TypedConstant constructorArgument in constructorArguments) - { - if (!SymbolEqualityComparer.Default.Equals(constructorArgument.Type, inheritanceBehaviorSymbol)) - { - continue; - } - - // It's an enum so it can't be null - RoslynDebug.Assert(constructorArgument.Value is not null); - - // We need to check that the inheritanceBehavior is not set to none and it's value inside the enum is zero - if ((int)constructorArgument.Value != 0) - { - isInheritanceModeSet = true; - break; - } - } - } - - if (!isInheritanceModeSet) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAGenericClassUnlessInheritanceModeSetRule, methodSymbol.Name)); - } - } - - if (methodSymbol.Parameters.Length > 0) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NoParametersRule, methodSymbol.Name)); - } - - if (methodSymbol.IsGenericMethod) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotGenericRule, methodSymbol.Name)); - } - - if (!methodSymbol.IsStatic) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(StaticRule, methodSymbol.Name)); - } - - if (methodSymbol.ReturnsVoid && methodSymbol.IsAsync) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAsyncVoidRule, methodSymbol.Name)); - } - - if (!methodSymbol.IsPublicAndHasCorrectResultantVisibility(canDiscoverInternals)) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(PublicRule, methodSymbol.Name)); - } - - if (!methodSymbol.ReturnsVoid - && (taskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, taskSymbol)) - && (valueTaskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, valueTaskSymbol))) + if (methodSymbol.IsClassInitializeMethod(classCleanupAttributeSymbol) + && !methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: true, + allowGenericType: methodSymbol.IsInheritanceModeSet(inheritanceBehaviorSymbol, classCleanupAttributeSymbol), testContextSymbol: null, + testClassAttributeSymbol, fixtureAllowInheritedTestClass: true, out bool isFixable)) { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(ReturnTypeRule, methodSymbol.Name)); + context.ReportDiagnostic(isFixable + ? methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name) + : methodSymbol.CreateDiagnostic(Rule, DiagnosticDescriptorHelper.CannotFixProperties, methodSymbol.Name)); } } } diff --git a/src/Analyzers/MSTest.Analyzers/ClassInitializeShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/ClassInitializeShouldBeValidAnalyzer.cs index 0805cde6c2..d99a209785 100644 --- a/src/Analyzers/MSTest.Analyzers/ClassInitializeShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/ClassInitializeShouldBeValidAnalyzer.cs @@ -3,7 +3,6 @@ using System.Collections.Immutable; -using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; @@ -13,32 +12,22 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0010: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class ClassInitializeShouldBeValidAnalyzer : DiagnosticAnalyzer { - private static readonly LocalizableResourceString Title = new(nameof(Resources.ClassInitializeShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableResourceString Description = new(nameof(Resources.ClassInitializeShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.ClassInitializeShouldBeValidMessageFormat_Public), Resources.ResourceManager, typeof(Resources)); - - internal static readonly DiagnosticDescriptor PublicRule = DiagnosticDescriptorHelper.Create( + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( DiagnosticIds.ClassInitializeShouldBeValidRuleId, - Title, - MessageFormat, - Description, + new LocalizableResourceString(nameof(Resources.ClassInitializeShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.ClassInitializeShouldBeValidMessageFormat), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.ClassInitializeShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)), Category.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true); - internal static readonly DiagnosticDescriptor StaticRule = PublicRule.WithMessage(new(nameof(Resources.ClassInitializeShouldBeValidMessageFormat_Static), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor SingleContextParameterRule = PublicRule.WithMessage(new(nameof(Resources.ClassInitializeShouldBeValidMessageFormat_SingleContextParameter), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor ReturnTypeRule = PublicRule.WithMessage(new(nameof(Resources.ClassInitializeShouldBeValidMessageFormat_ReturnType), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotAsyncVoidRule = PublicRule.WithMessage(new(nameof(Resources.ClassInitializeShouldBeValidMessageFormat_NotAsyncVoid), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotGenericRule = PublicRule.WithMessage(new(nameof(Resources.ClassInitializeShouldBeValidMessageFormat_NotGeneric), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor OrdinaryRule = PublicRule.WithMessage(new(nameof(Resources.ClassInitializeShouldBeValidMessageFormat_Ordinary), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotAGenericClassUnlessInheritanceModeSetRule = PublicRule.WithMessage(new(nameof(Resources.ClassInitializeShouldBeValidMessageFormat_NotAGenericClassUnlessInheritanceModeSet), Resources.ResourceManager, typeof(Resources))); - - public override ImmutableArray SupportedDiagnostics { get; } - = ImmutableArray.Create(PublicRule); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { @@ -47,105 +36,37 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { - if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassInitializeAttribute, out INamedTypeSymbol? classInitializeAttributeSymbol) - && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestContext, out INamedTypeSymbol? testContextSymbol)) + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassInitializeAttribute, out INamedTypeSymbol? classInitializeAttributeSymbol) + || !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestContext, out INamedTypeSymbol? testContextSymbol) + || !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute, out INamedTypeSymbol? testClassAttributeSymbol)) { - INamedTypeSymbol? taskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); - INamedTypeSymbol? inheritanceBehaviorSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingInheritanceBehavior); - INamedTypeSymbol? valueTaskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); - bool canDiscoverInternals = context.Compilation.CanDiscoverInternals(); - context.RegisterSymbolAction( - context => AnalyzeSymbol(context, classInitializeAttributeSymbol, taskSymbol, valueTaskSymbol, testContextSymbol, inheritanceBehaviorSymbol, canDiscoverInternals), - SymbolKind.Method); + return; } + + INamedTypeSymbol? taskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); + INamedTypeSymbol? inheritanceBehaviorSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingInheritanceBehavior); + INamedTypeSymbol? valueTaskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); + bool canDiscoverInternals = context.Compilation.CanDiscoverInternals(); + context.RegisterSymbolAction( + context => AnalyzeSymbol(context, classInitializeAttributeSymbol, taskSymbol, valueTaskSymbol, testContextSymbol, inheritanceBehaviorSymbol, testClassAttributeSymbol, canDiscoverInternals), + SymbolKind.Method); }); } private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol classInitializeAttributeSymbol, INamedTypeSymbol? taskSymbol, - INamedTypeSymbol? valueTaskSymbol, INamedTypeSymbol? testContextSymbol, INamedTypeSymbol? inheritanceBehaviorSymbol, bool canDiscoverInternals) + INamedTypeSymbol? valueTaskSymbol, INamedTypeSymbol testContextSymbol, INamedTypeSymbol? inheritanceBehaviorSymbol, INamedTypeSymbol testClassAttributeSymbol, + bool canDiscoverInternals) { var methodSymbol = (IMethodSymbol)context.Symbol; - if (!methodSymbol.IsClassInitializeMethod(classInitializeAttributeSymbol)) - { - return; - } - - if (methodSymbol.MethodKind != MethodKind.Ordinary) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(OrdinaryRule, methodSymbol.Name)); - - // Do not check the other criteria, users should fix the method kind first. - return; - } - - if (context.Symbol.ContainingType.IsGenericType) - { - bool isInheritanceModeSet = false; - foreach (AttributeData attr in methodSymbol.GetAttributes()) - { - if (!SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classInitializeAttributeSymbol)) - { - continue; - } - - ImmutableArray constructorArguments = attr.ConstructorArguments; - foreach (TypedConstant constructorArgument in constructorArguments) - { - if (!SymbolEqualityComparer.Default.Equals(constructorArgument.Type, inheritanceBehaviorSymbol)) - { - continue; - } - - // It's an enum so it can't be null - RoslynDebug.Assert(constructorArgument.Value is not null); - - // We need to check that the inheritanceBehavior is not set to none and it's value inside the enum is zero - if ((int)constructorArgument.Value != 0) - { - isInheritanceModeSet = true; - break; - } - } - } - - if (!isInheritanceModeSet) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAGenericClassUnlessInheritanceModeSetRule, methodSymbol.Name)); - } - } - - if (methodSymbol.Parameters.Length != 1 || testContextSymbol is null || - !SymbolEqualityComparer.Default.Equals(methodSymbol.Parameters[0].Type, testContextSymbol)) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(SingleContextParameterRule, methodSymbol.Name)); - } - - if (methodSymbol.IsGenericMethod) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotGenericRule, methodSymbol.Name)); - } - - if (!methodSymbol.IsStatic) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(StaticRule, methodSymbol.Name)); - } - - if (methodSymbol.ReturnsVoid && methodSymbol.IsAsync) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAsyncVoidRule, methodSymbol.Name)); - } - - if (!methodSymbol.IsPublicAndHasCorrectResultantVisibility(canDiscoverInternals)) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(PublicRule, methodSymbol.Name)); - } - - if (!methodSymbol.ReturnsVoid - && (taskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, taskSymbol)) - && (valueTaskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, valueTaskSymbol))) + if (methodSymbol.IsClassInitializeMethod(classInitializeAttributeSymbol) + && !methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: true, + allowGenericType: methodSymbol.IsInheritanceModeSet(inheritanceBehaviorSymbol, classInitializeAttributeSymbol), testContextSymbol, + testClassAttributeSymbol, fixtureAllowInheritedTestClass: true, out bool isFixable)) { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(ReturnTypeRule, methodSymbol.Name)); + context.ReportDiagnostic(isFixable + ? methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name) + : methodSymbol.CreateDiagnostic(Rule, DiagnosticDescriptorHelper.CannotFixProperties, methodSymbol.Name)); } } } diff --git a/src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs index 73ac939edd..3c63f35202 100644 --- a/src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0014: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class DataRowShouldBeValidAnalyzer : DiagnosticAnalyzer { @@ -151,7 +154,7 @@ private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeDat // on the one argument case. Check if we match either of the array argument constructors // and expand the array argument if we do. ImmutableArray constructorArguments = attribute.ConstructorArguments; - if (constructorArguments[0].Kind is TypedConstantKind.Array && !constructorArguments[0].IsNull) + if (attribute.AttributeConstructor?.Parameters.FirstOrDefault()?.IsParams == true) { constructorArguments = constructorArguments[0].Values; } @@ -219,7 +222,7 @@ private static bool IsArgumentCountMismatch(int constructorArgumentsLength, Immu { int optionalParametersCount = methodParameters.Count(x => x.HasExplicitDefaultValue); bool isLastParameterParams = methodParameters[^1].IsParams; - bool isOnlyParameterAndIsArray = methodParameters.Length == 1 && methodParameters[0].Type.Kind == SymbolKind.ArrayType; + bool isOnlyParameterAndIsArray = methodParameters is [{ Type.Kind: SymbolKind.ArrayType }]; if (isOnlyParameterAndIsArray) { diff --git a/src/Analyzers/MSTest.Analyzers/DoNotNegateBooleanAssertionAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DoNotNegateBooleanAssertionAnalyzer.cs index 5ae94cc96e..e46bf0cb56 100644 --- a/src/Analyzers/MSTest.Analyzers/DoNotNegateBooleanAssertionAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/DoNotNegateBooleanAssertionAnalyzer.cs @@ -13,6 +13,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0023: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class DoNotNegateBooleanAssertionAnalyzer : DiagnosticAnalyzer { @@ -56,7 +59,7 @@ private static void AnalyzeOperation(OperationAnalysisContext context, INamedTyp IArgumentOperation? conditionArgument = invocationOperation.Arguments.FirstOrDefault(x => x.Parameter?.Name == "condition"); if (conditionArgument != null - && conditionArgument.Children.Any(op => op is IUnaryOperation unary && unary.OperatorKind == UnaryOperatorKind.Not)) + && conditionArgument.Children.Any(op => op is IUnaryOperation { OperatorKind: UnaryOperatorKind.Not })) { context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule)); } diff --git a/src/Analyzers/MSTest.Analyzers/DoNotStoreStaticTestContextAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DoNotStoreStaticTestContextAnalyzer.cs index 97b17a88e4..2f298796fa 100644 --- a/src/Analyzers/MSTest.Analyzers/DoNotStoreStaticTestContextAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/DoNotStoreStaticTestContextAnalyzer.cs @@ -13,6 +13,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0024: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class DoNotStoreStaticTestContextAnalyzer : DiagnosticAnalyzer { @@ -49,9 +52,7 @@ private static void AnalyzeOperation(OperationAnalysisContext context, INamedTyp { var assignmentOperation = (ISimpleAssignmentOperation)context.Operation; - if (assignmentOperation.Target is IMemberReferenceOperation memberReferenceOperation - && memberReferenceOperation.Instance is null - && assignmentOperation.Value is IParameterReferenceOperation parameterReferenceOperation + if (assignmentOperation is { Target: IMemberReferenceOperation { Instance: null }, Value: IParameterReferenceOperation parameterReferenceOperation } && SymbolEqualityComparer.Default.Equals(parameterReferenceOperation.Type, testContextSymbol)) { context.ReportDiagnostic(assignmentOperation.CreateDiagnostic(Rule)); diff --git a/src/Analyzers/MSTest.Analyzers/DoNotUseSystemDescriptionAttributeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DoNotUseSystemDescriptionAttributeAnalyzer.cs new file mode 100644 index 0000000000..934ff5a22c --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/DoNotUseSystemDescriptionAttributeAnalyzer.cs @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0031: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class DoNotUseSystemDescriptionAttributeAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.DoNotUseSystemDescriptionAttributeTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.DoNotUseSystemDescriptionAttributeDescription), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.DoNotUseSystemDescriptionAttributeMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor DoNotUseSystemDescriptionAttributeRule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.DoNotUseSystemDescriptionAttributeRuleId, + Title, + MessageFormat, + Description, + Category.Usage, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(DoNotUseSystemDescriptionAttributeRule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestMethodAttribute, out INamedTypeSymbol? testMethodAttributeSymbol) + && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemDescriptionAttribute, out INamedTypeSymbol? systemDescriptionAttributeSymbol)) + { + context.RegisterSymbolAction( + context => AnalyzeSymbol(context, testMethodAttributeSymbol, systemDescriptionAttributeSymbol), + SymbolKind.Method); + } + }); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testMethodAttributeSymbol, INamedTypeSymbol systemDescriptionAttributeSymbol) + { + var methodSymbol = (IMethodSymbol)context.Symbol; + + bool hasTestMethodAttribute = false; + bool hasSystemDescriptionAttribute = false; + foreach (AttributeData attribute in methodSymbol.GetAttributes()) + { + if (attribute.AttributeClass.Inherits(testMethodAttributeSymbol)) + { + hasTestMethodAttribute = true; + } + + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, systemDescriptionAttributeSymbol)) + { + hasSystemDescriptionAttribute = true; + } + + if (hasTestMethodAttribute && hasSystemDescriptionAttribute) + { + context.ReportDiagnostic(methodSymbol.CreateDiagnostic(DoNotUseSystemDescriptionAttributeRule, methodSymbol.Name)); + break; + } + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticDescriptorHelper.cs b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticDescriptorHelper.cs index 9d3bb759d6..999c777eb9 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticDescriptorHelper.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticDescriptorHelper.cs @@ -1,12 +1,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Collections.Immutable; + using Microsoft.CodeAnalysis; namespace MSTest.Analyzers.Helpers; internal static class DiagnosticDescriptorHelper { + public const string CannotFixPropertyKey = "CannotFix"; + public static readonly ImmutableDictionary CannotFixProperties + = ImmutableDictionary.Empty.Add(CannotFixPropertyKey, null); + public static DiagnosticDescriptor Create( string id, LocalizableString title, diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs index 48f07cfc7f..6d5ff9211e 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs @@ -29,4 +29,11 @@ internal static class DiagnosticIds public const string DoNotNegateBooleanAssertionRuleId = "MSTEST0023"; public const string DoNotStoreStaticTestContextAnalyzerRuleId = "MSTEST0024"; public const string PreferAssertFailOverAlwaysFalseConditionsRuleId = "MSTEST0025"; + public const string AssertionArgsShouldAvoidConditionalAccessRuleId = "MSTEST0026"; + public const string UseAsyncSuffixTestMethodSuppressorRuleId = "MSTEST0027"; + public const string UseAsyncSuffixTestFixtureMethodSuppressorRuleId = "MSTEST0028"; + public const string PublicMethodShouldBeTestMethodRuleId = "MSTEST0029"; + public const string TypeContainingTestMethodShouldBeATestClassRuleId = "MSTEST0030"; + public const string DoNotUseSystemDescriptionAttributeRuleId = "MSTEST0031"; + public const string ReviewAlwaysTrueAssertConditionAnalyzerRuleId = "MSTEST0032"; } diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/FixtureUtils.cs b/src/Analyzers/MSTest.Analyzers/Helpers/FixtureUtils.cs new file mode 100644 index 0000000000..4f79378743 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/Helpers/FixtureUtils.cs @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; + +namespace MSTest.Analyzers.Helpers; + +internal static class FixtureUtils +{ + public static bool IsAssemblyInitializeMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol assemblyInitializeAttributeSymbol) + => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, assemblyInitializeAttributeSymbol)); + + public static bool IsAssemblyCleanupMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol assemblyCleanupAttributeSymbol) + => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, assemblyCleanupAttributeSymbol)); + + public static bool IsClassInitializeMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol classInitializeAttributeSymbol) + => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classInitializeAttributeSymbol)); + + public static bool IsClassCleanupMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol classCleanupAttributeSymbol) + => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classCleanupAttributeSymbol)); + + public static bool IsTestInitializeMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol testInitializeAttributeSymbol) + => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, testInitializeAttributeSymbol)); + + public static bool IsTestCleanupMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol testCleanupAttributeSymbol) + => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, testCleanupAttributeSymbol)); + + public static bool HasValidFixtureMethodSignature(this IMethodSymbol methodSymbol, INamedTypeSymbol? taskSymbol, + INamedTypeSymbol? valueTaskSymbol, bool canDiscoverInternals, bool shouldBeStatic, bool allowGenericType, + INamedTypeSymbol? testContextSymbol, INamedTypeSymbol testClassAttributeSymbol, bool fixtureAllowInheritedTestClass, out bool isFixable) + { + isFixable = false; + if (methodSymbol.MethodKind != MethodKind.Ordinary + || (methodSymbol.ContainingType.IsGenericType && !allowGenericType)) + { + return false; + } + + // Fixtures are only supported on classes + if (methodSymbol.ContainingType.TypeKind != TypeKind.Class) + { + return false; + } + + // For AssemblyInitialize and AssemblyCleanup methods, the containing class should be marked with TestClassAttribute. + // For the other fixtures, it's only required if the type is not sealed. + if ((!fixtureAllowInheritedTestClass || methodSymbol.ContainingType.IsSealed) + && !methodSymbol.ContainingType.GetAttributes().Any(x => x.AttributeClass.Inherits(testClassAttributeSymbol))) + { + return false; + } + + // Validate the method signature + isFixable = true; + return !methodSymbol.IsGenericMethod + && methodSymbol.IsStatic == shouldBeStatic + && !methodSymbol.IsAbstract + && HasCorrectParameters(methodSymbol, testContextSymbol) + && methodSymbol.IsPublicAndHasCorrectResultantVisibility(canDiscoverInternals) + && HasValidReturnType(methodSymbol, taskSymbol, valueTaskSymbol); + } + + public static bool HasValidTestMethodSignature(this IMethodSymbol methodSymbol, INamedTypeSymbol? taskSymbol, + INamedTypeSymbol? valueTaskSymbol, bool canDiscoverInternals) + { + if (methodSymbol.MethodKind != MethodKind.Ordinary + || methodSymbol.IsGenericMethod + || methodSymbol.IsStatic + || methodSymbol.IsAbstract) + { + return false; + } + + if (methodSymbol.GetResultantVisibility() is { } resultantVisibility) + { + if (!canDiscoverInternals && (resultantVisibility != SymbolVisibility.Public || methodSymbol.DeclaredAccessibility != Accessibility.Public)) + { + return false; + } + else if (canDiscoverInternals && resultantVisibility == SymbolVisibility.Private) + { + return false; + } + } + + return methodSymbol is { ReturnsVoid: true, IsAsync: false } + || SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, taskSymbol) + || SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, valueTaskSymbol); + } + + public static bool IsInheritanceModeSet(this IMethodSymbol methodSymbol, INamedTypeSymbol? inheritanceBehaviorSymbol, + INamedTypeSymbol? classInitializeOrCleanupAttributeSymbol) + { + foreach (AttributeData attr in methodSymbol.GetAttributes()) + { + if (!SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classInitializeOrCleanupAttributeSymbol)) + { + continue; + } + + ImmutableArray constructorArguments = attr.ConstructorArguments; + foreach (TypedConstant constructorArgument in constructorArguments) + { + if (!SymbolEqualityComparer.Default.Equals(constructorArgument.Type, inheritanceBehaviorSymbol)) + { + continue; + } + + // It's an enum so it can't be null + RoslynDebug.Assert(constructorArgument.Value is not null); + + // We need to check that the inheritanceBehavior is not set to none and it's value inside the enum is zero + if ((int)constructorArgument.Value != 0) + { + return true; + } + } + } + + return false; + } + + private static bool HasCorrectParameters(IMethodSymbol methodSymbol, INamedTypeSymbol? testContextSymbol) + => testContextSymbol is null + ? methodSymbol.Parameters.Length == 0 + : methodSymbol.Parameters.Length == 1 && SymbolEqualityComparer.Default.Equals(methodSymbol.Parameters[0].Type, testContextSymbol); + + private static bool HasValidReturnType(IMethodSymbol methodSymbol, INamedTypeSymbol? taskSymbol, INamedTypeSymbol? valueTaskSymbol) + => methodSymbol is { ReturnsVoid: true, IsAsync: false } + || (taskSymbol is not null && SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, taskSymbol)) + || (valueTaskSymbol is not null && SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, valueTaskSymbol)); +} diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/IMethodSymbolExtensions.cs b/src/Analyzers/MSTest.Analyzers/Helpers/IMethodSymbolExtensions.cs index a249c1e385..6d58c445e5 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/IMethodSymbolExtensions.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/IMethodSymbolExtensions.cs @@ -22,22 +22,4 @@ public static bool IsPublicAndHasCorrectResultantVisibility(this IMethodSymbol m ? resultantVisibility is SymbolVisibility.Public or SymbolVisibility.Internal : resultantVisibility is SymbolVisibility.Public; } - - public static bool IsAssemblyInitializeMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol assemblyInitializeAttributeSymbol) - => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, assemblyInitializeAttributeSymbol)); - - public static bool IsAssemblyCleanupMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol assemblyCleanupAttributeSymbol) - => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, assemblyCleanupAttributeSymbol)); - - public static bool IsClassInitializeMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol classInitializeAttributeSymbol) - => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classInitializeAttributeSymbol)); - - public static bool IsClassCleanupMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol classCleanupAttributeSymbol) - => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classCleanupAttributeSymbol)); - - public static bool IsTestInitializeMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol testInitializeAttributeSymbol) - => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, testInitializeAttributeSymbol)); - - public static bool IsTestCleanupMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol testCleanupAttributeSymbol) - => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, testCleanupAttributeSymbol)); } diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/ISymbolExtensions.cs b/src/Analyzers/MSTest.Analyzers/Helpers/ISymbolExtensions.cs new file mode 100644 index 0000000000..041990ae0b --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/Helpers/ISymbolExtensions.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.CodeAnalysis; + +namespace MSTest.Analyzers.Helpers; + +internal static class ISymbolExtensions +{ + public static ITypeSymbol? GetReferencedMemberOrLocalOrParameter(this ISymbol? symbol) => symbol switch + { + IEventSymbol eventSymbol => eventSymbol.Type, + + IFieldSymbol fieldSymbol => fieldSymbol.Type, + + IMethodSymbol methodSymbol => methodSymbol.ReturnType, + + IPropertySymbol propertySymbol => propertySymbol.Type, + + ILocalSymbol localSymbol => localSymbol.Type, + + _ => null, + }; +} diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs index e4d17ffffd..1cfd24c9e6 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs @@ -11,6 +11,7 @@ internal static class WellKnownTypeNames public const string MicrosoftVisualStudioTestToolsUnitTestingAssert = "Microsoft.VisualStudio.TestTools.UnitTesting.Assert"; public const string MicrosoftVisualStudioTestToolsUnitTestingClassCleanupAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingClassInitializeAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.ClassInitializeAttribute"; + public const string MicrosoftVisualStudioTestToolsUnitTestingCollectionAssert = "Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert"; public const string MicrosoftVisualStudioTestToolsUnitTestingCssIterationAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.CssIterationAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingCssProjectStructureAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.CssProjectStructureAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingDataRowAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute"; @@ -23,6 +24,7 @@ internal static class WellKnownTypeNames public const string MicrosoftVisualStudioTestToolsUnitTestingOwnerAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.OwnerAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingParallelizeAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.ParallelizeAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingPriorityAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.PriorityAttribute"; + public const string MicrosoftVisualStudioTestToolsUnitTestingStringAssert = "Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert"; public const string MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingTestCleanupAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingTestContext = "Microsoft.VisualStudio.TestTools.UnitTesting.TestContext"; @@ -31,9 +33,12 @@ internal static class WellKnownTypeNames public const string MicrosoftVisualStudioTestToolsUnitTestingTestPropertyAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingWorkItemAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.WorkItemAttribute"; + public const string SystemDescriptionAttribute = "System.ComponentModel.DescriptionAttribute"; public const string SystemIAsyncDisposable = "System.IAsyncDisposable"; public const string SystemIDisposable = "System.IDisposable"; + public const string SystemNullable = "System.Nullable`1"; public const string SystemThreadingTasksTask = "System.Threading.Tasks.Task"; public const string SystemThreadingTasksTask1 = "System.Threading.Tasks.Task`1"; public const string SystemThreadingTasksValueTask = "System.Threading.Tasks.ValueTask"; + public const string SystemThreadingTasksValueTask1 = "System.Threading.Tasks.ValueTask`1"; } diff --git a/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj b/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj index 652a5e0775..b7ded067c4 100644 --- a/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj +++ b/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -11,17 +11,18 @@ - - + + + diff --git a/src/Analyzers/MSTest.Analyzers/PreferAssertFailOverAlwaysFalseConditionsAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PreferAssertFailOverAlwaysFalseConditionsAnalyzer.cs index 1a689e87ec..ec11b318ed 100644 --- a/src/Analyzers/MSTest.Analyzers/PreferAssertFailOverAlwaysFalseConditionsAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/PreferAssertFailOverAlwaysFalseConditionsAnalyzer.cs @@ -10,9 +10,13 @@ using Microsoft.CodeAnalysis.Operations; using MSTest.Analyzers.Helpers; +using MSTest.Analyzers.RoslynAnalyzerHelpers; namespace MSTest.Analyzers; +/// +/// MSTEST0025: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class PreferAssertFailOverAlwaysFalseConditionsAnalyzer : DiagnosticAnalyzer { @@ -53,24 +57,26 @@ public override void Initialize(AnalysisContext context) { Compilation compilation = context.Compilation; INamedTypeSymbol? assertSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssert); + INamedTypeSymbol? nullableSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemNullable); if (assertSymbol is not null) { - context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol), OperationKind.Invocation); + context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol, nullableSymbol), OperationKind.Invocation); } }); } - private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol) + private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol, INamedTypeSymbol? nullableSymbol) { var operation = (IInvocationOperation)context.Operation; - if (assertSymbol.Equals(operation.TargetMethod?.ContainingType, SymbolEqualityComparer.Default) && - IsAlwaysFalse(operation)) + + if (assertSymbol.Equals(operation.TargetMethod.ContainingType, SymbolEqualityComparer.Default) && + IsAlwaysFalse(operation, nullableSymbol)) { context.ReportDiagnostic(operation.CreateDiagnostic(Rule, operation.TargetMethod.Name)); } } - private static bool IsAlwaysFalse(IInvocationOperation operation) + private static bool IsAlwaysFalse(IInvocationOperation operation, INamedTypeSymbol? nullableSymbol) => operation.TargetMethod.Name switch { "IsTrue" => GetConditionArgument(operation) is { Value.ConstantValue: { HasValue: true, Value: false } }, @@ -78,9 +84,18 @@ private static bool IsAlwaysFalse(IInvocationOperation operation) "AreEqual" => GetEqualityStatus(operation, ExpectedParameterName) == EqualityStatus.NotEqual, "AreNotEqual" => GetEqualityStatus(operation, NotExpectedParameterName) == EqualityStatus.Equal, "IsNotNull" => GetValueArgument(operation) is { Value.ConstantValue: { HasValue: true, Value: null } }, + "IsNull" => GetValueArgument(operation) is { } valueArgumentOperation && IsNotNullableType(valueArgumentOperation, nullableSymbol), _ => false, }; + private static bool IsNotNullableType(IArgumentOperation valueArgumentOperation, INamedTypeSymbol? nullableSymbol) + { + ITypeSymbol? valueArgType = valueArgumentOperation.Value.GetReferencedMemberOrLocalOrParameter().GetReferencedMemberOrLocalOrParameter(); + return valueArgType is not null + && valueArgType.NullableAnnotation == NullableAnnotation.NotAnnotated + && !SymbolEqualityComparer.IncludeNullability.Equals(valueArgType.OriginalDefinition, nullableSymbol); + } + private static IArgumentOperation? GetArgumentWithName(IInvocationOperation operation, string name) => operation.Arguments.FirstOrDefault(arg => arg.Parameter?.Name == name); diff --git a/src/Analyzers/MSTest.Analyzers/PreferConstructorOverTestInitializeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PreferConstructorOverTestInitializeAnalyzer.cs index 32c8bce9c1..61d03d2294 100644 --- a/src/Analyzers/MSTest.Analyzers/PreferConstructorOverTestInitializeAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/PreferConstructorOverTestInitializeAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0020: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class PreferConstructorOverTestInitializeAnalyzer : DiagnosticAnalyzer { diff --git a/src/Analyzers/MSTest.Analyzers/PreferDisposeOverTestCleanupAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PreferDisposeOverTestCleanupAnalyzer.cs index f2b35cbb47..404d4cf7ca 100644 --- a/src/Analyzers/MSTest.Analyzers/PreferDisposeOverTestCleanupAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/PreferDisposeOverTestCleanupAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0021: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class PreferDisposeOverTestCleanupAnalyzer : DiagnosticAnalyzer { diff --git a/src/Analyzers/MSTest.Analyzers/PreferTestCleanupOverDisposeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PreferTestCleanupOverDisposeAnalyzer.cs index 3c8f092d9d..1e22fec61a 100644 --- a/src/Analyzers/MSTest.Analyzers/PreferTestCleanupOverDisposeAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/PreferTestCleanupOverDisposeAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0022: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class PreferTestCleanupOverDisposeAnalyzer : DiagnosticAnalyzer { diff --git a/src/Analyzers/MSTest.Analyzers/PreferTestInitializeOverConstructorAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PreferTestInitializeOverConstructorAnalyzer.cs index 48635e25b1..676125627e 100644 --- a/src/Analyzers/MSTest.Analyzers/PreferTestInitializeOverConstructorAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/PreferTestInitializeOverConstructorAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0019: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class PreferTestInitializeOverConstructorAnalyzer : DiagnosticAnalyzer { diff --git a/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt b/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..ab058de62d --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt b/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..4d8fce9705 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt @@ -0,0 +1,130 @@ +#nullable enable +Analyzer.Utilities.WellKnownTypeProvider +Analyzer.Utilities.WellKnownTypeProvider.Compilation.get -> Microsoft.CodeAnalysis.Compilation! +Analyzer.Utilities.WellKnownTypeProvider.GetOrCreateTypeByMetadataName(string! fullTypeName) -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Analyzer.Utilities.WellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(string! fullTypeName, out Microsoft.CodeAnalysis.INamedTypeSymbol? namedTypeSymbol) -> bool +MSTest.Analyzers.AssemblyCleanupShouldBeValidAnalyzer +MSTest.Analyzers.AssemblyCleanupShouldBeValidAnalyzer.AssemblyCleanupShouldBeValidAnalyzer() -> void +MSTest.Analyzers.AssemblyInitializeShouldBeValidAnalyzer +MSTest.Analyzers.AssemblyInitializeShouldBeValidAnalyzer.AssemblyInitializeShouldBeValidAnalyzer() -> void +MSTest.Analyzers.AssertionArgsShouldAvoidConditionalAccessAnalyzer +MSTest.Analyzers.AssertionArgsShouldAvoidConditionalAccessAnalyzer.AssertionArgsShouldAvoidConditionalAccessAnalyzer() -> void +MSTest.Analyzers.AssertionArgsShouldBePassedInCorrectOrderAnalyzer +MSTest.Analyzers.AssertionArgsShouldBePassedInCorrectOrderAnalyzer.AssertionArgsShouldBePassedInCorrectOrderAnalyzer() -> void +MSTest.Analyzers.AvoidExpectedExceptionAttributeAnalyzer +MSTest.Analyzers.AvoidExpectedExceptionAttributeAnalyzer.AvoidExpectedExceptionAttributeAnalyzer() -> void +MSTest.Analyzers.ClassCleanupShouldBeValidAnalyzer +MSTest.Analyzers.ClassCleanupShouldBeValidAnalyzer.ClassCleanupShouldBeValidAnalyzer() -> void +MSTest.Analyzers.ClassInitializeShouldBeValidAnalyzer +MSTest.Analyzers.ClassInitializeShouldBeValidAnalyzer.ClassInitializeShouldBeValidAnalyzer() -> void +MSTest.Analyzers.DataRowShouldBeValidAnalyzer +MSTest.Analyzers.DataRowShouldBeValidAnalyzer.DataRowShouldBeValidAnalyzer() -> void +MSTest.Analyzers.DoNotNegateBooleanAssertionAnalyzer +MSTest.Analyzers.DoNotNegateBooleanAssertionAnalyzer.DoNotNegateBooleanAssertionAnalyzer() -> void +MSTest.Analyzers.DoNotStoreStaticTestContextAnalyzer +MSTest.Analyzers.DoNotStoreStaticTestContextAnalyzer.DoNotStoreStaticTestContextAnalyzer() -> void +MSTest.Analyzers.DoNotUseSystemDescriptionAttributeAnalyzer +MSTest.Analyzers.DoNotUseSystemDescriptionAttributeAnalyzer.DoNotUseSystemDescriptionAttributeAnalyzer() -> void +MSTest.Analyzers.PreferAssertFailOverAlwaysFalseConditionsAnalyzer +MSTest.Analyzers.PreferAssertFailOverAlwaysFalseConditionsAnalyzer.PreferAssertFailOverAlwaysFalseConditionsAnalyzer() -> void +MSTest.Analyzers.PreferConstructorOverTestInitializeAnalyzer +MSTest.Analyzers.PreferConstructorOverTestInitializeAnalyzer.PreferConstructorOverTestInitializeAnalyzer() -> void +MSTest.Analyzers.PreferDisposeOverTestCleanupAnalyzer +MSTest.Analyzers.PreferDisposeOverTestCleanupAnalyzer.PreferDisposeOverTestCleanupAnalyzer() -> void +MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer +MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer.ReviewAlwaysTrueAssertConditionAnalyzer() -> void +MSTest.Analyzers.PreferTestCleanupOverDisposeAnalyzer +MSTest.Analyzers.PreferTestCleanupOverDisposeAnalyzer.PreferTestCleanupOverDisposeAnalyzer() -> void +MSTest.Analyzers.PreferTestInitializeOverConstructorAnalyzer +MSTest.Analyzers.PreferTestInitializeOverConstructorAnalyzer.PreferTestInitializeOverConstructorAnalyzer() -> void +MSTest.Analyzers.PublicMethodShouldBeTestMethodAnalyzer +MSTest.Analyzers.PublicMethodShouldBeTestMethodAnalyzer.PublicMethodShouldBeTestMethodAnalyzer() -> void +MSTest.Analyzers.PublicTypeShouldBeTestClassAnalyzer +MSTest.Analyzers.PublicTypeShouldBeTestClassAnalyzer.PublicTypeShouldBeTestClassAnalyzer() -> void +MSTest.Analyzers.TestClassShouldBeValidAnalyzer +MSTest.Analyzers.TestClassShouldBeValidAnalyzer.TestClassShouldBeValidAnalyzer() -> void +MSTest.Analyzers.TestClassShouldHaveTestMethodAnalyzer +MSTest.Analyzers.TestClassShouldHaveTestMethodAnalyzer.TestClassShouldHaveTestMethodAnalyzer() -> void +MSTest.Analyzers.TestCleanupShouldBeValidAnalyzer +MSTest.Analyzers.TestCleanupShouldBeValidAnalyzer.TestCleanupShouldBeValidAnalyzer() -> void +MSTest.Analyzers.TestContextShouldBeValidAnalyzer +MSTest.Analyzers.TestContextShouldBeValidAnalyzer.TestContextShouldBeValidAnalyzer() -> void +MSTest.Analyzers.TestInitializeShouldBeValidAnalyzer +MSTest.Analyzers.TestInitializeShouldBeValidAnalyzer.TestInitializeShouldBeValidAnalyzer() -> void +MSTest.Analyzers.TestMethodShouldBeValidAnalyzer +MSTest.Analyzers.TestMethodShouldBeValidAnalyzer.TestMethodShouldBeValidAnalyzer() -> void +MSTest.Analyzers.TestMethodShouldNotBeIgnoredAnalyzer +MSTest.Analyzers.TestMethodShouldNotBeIgnoredAnalyzer.TestMethodShouldNotBeIgnoredAnalyzer() -> void +MSTest.Analyzers.TypeContainingTestMethodShouldBeATestClassAnalyzer +MSTest.Analyzers.TypeContainingTestMethodShouldBeATestClassAnalyzer.TypeContainingTestMethodShouldBeATestClassAnalyzer() -> void +MSTest.Analyzers.UseAsyncSuffixTestFixtureMethodSuppressor +MSTest.Analyzers.UseAsyncSuffixTestFixtureMethodSuppressor.UseAsyncSuffixTestFixtureMethodSuppressor() -> void +MSTest.Analyzers.UseAsyncSuffixTestMethodSuppressor +MSTest.Analyzers.UseAsyncSuffixTestMethodSuppressor.UseAsyncSuffixTestMethodSuppressor() -> void +MSTest.Analyzers.UseAttributeOnTestMethodAnalyzer +MSTest.Analyzers.UseAttributeOnTestMethodAnalyzer.UseAttributeOnTestMethodAnalyzer() -> void +MSTest.Analyzers.UseParallelizeAttributeAnalyzer +MSTest.Analyzers.UseParallelizeAttributeAnalyzer.UseParallelizeAttributeAnalyzer() -> void +override MSTest.Analyzers.AssemblyCleanupShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.AssemblyCleanupShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.AssemblyInitializeShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.AssemblyInitializeShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.AssertionArgsShouldAvoidConditionalAccessAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.AssertionArgsShouldAvoidConditionalAccessAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.AssertionArgsShouldBePassedInCorrectOrderAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.AssertionArgsShouldBePassedInCorrectOrderAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.AvoidExpectedExceptionAttributeAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.AvoidExpectedExceptionAttributeAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.ClassCleanupShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.ClassCleanupShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.ClassInitializeShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.ClassInitializeShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.DataRowShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.DataRowShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.DoNotNegateBooleanAssertionAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.DoNotNegateBooleanAssertionAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.DoNotStoreStaticTestContextAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.DoNotStoreStaticTestContextAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.DoNotUseSystemDescriptionAttributeAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.DoNotUseSystemDescriptionAttributeAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.PreferAssertFailOverAlwaysFalseConditionsAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.PreferAssertFailOverAlwaysFalseConditionsAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.PreferConstructorOverTestInitializeAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.PreferConstructorOverTestInitializeAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.PreferDisposeOverTestCleanupAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.PreferDisposeOverTestCleanupAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.PreferTestCleanupOverDisposeAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.PreferTestCleanupOverDisposeAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.PreferTestInitializeOverConstructorAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.PreferTestInitializeOverConstructorAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.PublicMethodShouldBeTestMethodAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.PublicMethodShouldBeTestMethodAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.PublicTypeShouldBeTestClassAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.PublicTypeShouldBeTestClassAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.TestClassShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.TestClassShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.TestClassShouldHaveTestMethodAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.TestClassShouldHaveTestMethodAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.TestCleanupShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.TestCleanupShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.TestContextShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.TestContextShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.TestInitializeShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.TestInitializeShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.TestMethodShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.TestMethodShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.TestMethodShouldNotBeIgnoredAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.TestMethodShouldNotBeIgnoredAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.TypeContainingTestMethodShouldBeATestClassAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.TypeContainingTestMethodShouldBeATestClassAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.UseAsyncSuffixTestFixtureMethodSuppressor.ReportSuppressions(Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext context) -> void +override MSTest.Analyzers.UseAsyncSuffixTestFixtureMethodSuppressor.SupportedSuppressions.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.UseAsyncSuffixTestMethodSuppressor.ReportSuppressions(Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext context) -> void +override MSTest.Analyzers.UseAsyncSuffixTestMethodSuppressor.SupportedSuppressions.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.UseAttributeOnTestMethodAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.UseAttributeOnTestMethodAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.UseParallelizeAttributeAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.UseParallelizeAttributeAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +static Analyzer.Utilities.WellKnownTypeProvider.GetOrCreate(Microsoft.CodeAnalysis.Compilation! compilation) -> Analyzer.Utilities.WellKnownTypeProvider! diff --git a/src/Analyzers/MSTest.Analyzers/PublicMethodShouldBeTestMethodAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PublicMethodShouldBeTestMethodAnalyzer.cs new file mode 100644 index 0000000000..f971868a17 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/PublicMethodShouldBeTestMethodAnalyzer.cs @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0029: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class PublicMethodShouldBeTestMethodAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.PublicMethodShouldBeTestMethodAnalyzerTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.PublicMethodShouldBeTestMethodAnalyzerDescription), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.PublicMethodShouldBeTestMethodAnalyzerFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor PublicMethodShouldBeTestMethodRule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.PublicMethodShouldBeTestMethodRuleId, + Title, + MessageFormat, + Description, + Category.Design, + DiagnosticSeverity.Info, + isEnabledByDefault: false); + + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(PublicMethodShouldBeTestMethodRule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(context => + { + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestMethodAttribute, out INamedTypeSymbol? testMethodAttributeSymbol) + && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute, out INamedTypeSymbol? testClassAttributeSymbol)) + { + bool canDiscoverInternals = context.Compilation.CanDiscoverInternals(); + INamedTypeSymbol? taskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); + INamedTypeSymbol? valueTaskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); + context.RegisterSymbolAction( + context => AnalyzeSymbol(context, testMethodAttributeSymbol, testClassAttributeSymbol, taskSymbol, valueTaskSymbol, canDiscoverInternals), + SymbolKind.Method); + } + }); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testMethodAttributeSymbol, INamedTypeSymbol testClassAttributeSymbol, INamedTypeSymbol? taskSymbol, + INamedTypeSymbol? valueTaskSymbol, bool canDiscoverInternals) + { + var methodSymbol = (IMethodSymbol)context.Symbol; + if (methodSymbol.GetResultantVisibility() != SymbolVisibility.Public) + { + return; + } + + if (!methodSymbol.HasValidTestMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals)) + { + return; + } + + INamedTypeSymbol containingTypeSymbol = context.Symbol.ContainingType; + bool isTestClass = false; + foreach (AttributeData classAttribute in containingTypeSymbol.GetAttributes()) + { + if (classAttribute.AttributeClass.Inherits(testClassAttributeSymbol)) + { + isTestClass = true; + break; + } + } + + if (!isTestClass) + { + return; + } + + ImmutableArray methodAttributes = methodSymbol.GetAttributes(); + bool isTestMethod = false; + foreach (AttributeData methodAttribute in methodAttributes) + { + // Check if method is a test method or inherit from the TestMethod attribute. + if (methodAttribute.AttributeClass.Inherits(testMethodAttributeSymbol)) + { + isTestMethod = true; + } + } + + if (isTestMethod) + { + return; + } + + context.ReportDiagnostic(methodSymbol.CreateDiagnostic(PublicMethodShouldBeTestMethodRule, methodSymbol.Name)); + } +} diff --git a/src/Analyzers/MSTest.Analyzers/PublicTypeShouldBeTestClassAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PublicTypeShouldBeTestClassAnalyzer.cs index ba7a9ff32e..07d03b206c 100644 --- a/src/Analyzers/MSTest.Analyzers/PublicTypeShouldBeTestClassAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/PublicTypeShouldBeTestClassAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0004: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class PublicTypeShouldBeTestClassAnalyzer : DiagnosticAnalyzer { @@ -48,7 +51,10 @@ public override void Initialize(AnalysisContext context) private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testClassAttributeSymbol) { var namedTypeSymbol = (INamedTypeSymbol)context.Symbol; - if (namedTypeSymbol.DeclaredAccessibility != Accessibility.Public + if (namedTypeSymbol.IsAbstract + || namedTypeSymbol.IsStatic + || namedTypeSymbol.TypeKind != TypeKind.Class + || namedTypeSymbol.DeclaredAccessibility != Accessibility.Public || namedTypeSymbol.GetResultantVisibility() != SymbolVisibility.Public) { return; diff --git a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs index c01c7554c8..1543495a4a 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs +++ b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs @@ -62,13 +62,13 @@ internal Resources() { /// /// Looks up a localized string similar to Methods marked with [AssemblyCleanup] should follow the following layout to be valid: - ///- it should be 'public' - ///- it should be 'static' - ///- it should not be generic - ///- it should not take any parameter + ///- be 'public' + ///- be 'static' + ///- not be generic nor defined on a generic class + ///- not take any parameter ///- return type should be 'void', 'Task' or 'ValueTask' - ///- it should not be 'async void' - ///- it should not be a special method (finalizer, operator...).. + ///- not be 'async void' + ///- not be a special method (finalizer, operator...).. /// internal static string AssemblyCleanupShouldBeValidDescription { get { @@ -77,74 +77,11 @@ internal static string AssemblyCleanupShouldBeValidDescription { } /// - /// Looks up a localized string similar to AssemblyCleanup method '{0}' should not take any parameter. - /// - internal static string AssemblyCleanupShouldBeValidMessageFormat_NoParameters { - get { - return ResourceManager.GetString("AssemblyCleanupShouldBeValidMessageFormat_NoParameters", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyCleanup method '{0}' can't be declared on a generic class. - /// - internal static string AssemblyCleanupShouldBeValidMessageFormat_NotAGenericClass { - get { - return ResourceManager.GetString("AssemblyCleanupShouldBeValidMessageFormat_NotAGenericClass", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask'. - /// - internal static string AssemblyCleanupShouldBeValidMessageFormat_NotAsyncVoid { - get { - return ResourceManager.GetString("AssemblyCleanupShouldBeValidMessageFormat_NotAsyncVoid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyCleanup method '{0}' should not be generic. - /// - internal static string AssemblyCleanupShouldBeValidMessageFormat_NotGeneric { - get { - return ResourceManager.GetString("AssemblyCleanupShouldBeValidMessageFormat_NotGeneric", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyCleanup method '{0}' should be an 'ordinary' method. - /// - internal static string AssemblyCleanupShouldBeValidMessageFormat_Ordinary { - get { - return ResourceManager.GetString("AssemblyCleanupShouldBeValidMessageFormat_Ordinary", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyCleanup method '{0}' should be 'public'. + /// Looks up a localized string similar to AssemblyCleanup method '{0}' signature is invalid. /// - internal static string AssemblyCleanupShouldBeValidMessageFormat_Public { + internal static string AssemblyCleanupShouldBeValidMessageFormat { get { - return ResourceManager.GetString("AssemblyCleanupShouldBeValidMessageFormat_Public", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask'. - /// - internal static string AssemblyCleanupShouldBeValidMessageFormat_ReturnType { - get { - return ResourceManager.GetString("AssemblyCleanupShouldBeValidMessageFormat_ReturnType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyCleanup method '{0}' should be 'static'. - /// - internal static string AssemblyCleanupShouldBeValidMessageFormat_Static { - get { - return ResourceManager.GetString("AssemblyCleanupShouldBeValidMessageFormat_Static", resourceCulture); + return ResourceManager.GetString("AssemblyCleanupShouldBeValidMessageFormat", resourceCulture); } } @@ -159,13 +96,13 @@ internal static string AssemblyCleanupShouldBeValidTitle { /// /// Looks up a localized string similar to Methods marked with [AssemblyInitialize] should follow the following layout to be valid: - ///- it should be 'public' - ///- it should be 'static' - ///- it should not be generic - ///- it should take one parameter of type 'TestContext' + ///- be 'public' + ///- be 'static' + ///- not be generic nor be defined on a generic class + ///- take a single parameter of type 'TestContext' ///- return type should be 'void', 'Task' or 'ValueTask' - ///- it should not be 'async void' - ///- it should not be a special method (finalizer, operator...).. + ///- not be 'async void' + ///- not be a special method (finalizer, operator...).. /// internal static string AssemblyInitializeShouldBeValidDescription { get { @@ -174,83 +111,38 @@ internal static string AssemblyInitializeShouldBeValidDescription { } /// - /// Looks up a localized string similar to AssemblyInitialize method '{0}' can't be declared on a generic class. - /// - internal static string AssemblyInitializeShouldBeValidMessageFormat_NotAGenericClass { - get { - return ResourceManager.GetString("AssemblyInitializeShouldBeValidMessageFormat_NotAGenericClass", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask'. - /// - internal static string AssemblyInitializeShouldBeValidMessageFormat_NotAsyncVoid { - get { - return ResourceManager.GetString("AssemblyInitializeShouldBeValidMessageFormat_NotAsyncVoid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyInitialize method '{0}' should not be generic. - /// - internal static string AssemblyInitializeShouldBeValidMessageFormat_NotGeneric { - get { - return ResourceManager.GetString("AssemblyInitializeShouldBeValidMessageFormat_NotGeneric", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyInitialize method '{0}' should be an 'ordinary' method. + /// Looks up a localized string similar to AssemblyInitialize method '{0}' signature is invalid. /// - internal static string AssemblyInitializeShouldBeValidMessageFormat_Ordinary { + internal static string AssemblyInitializeShouldBeValidMessageFormat { get { - return ResourceManager.GetString("AssemblyInitializeShouldBeValidMessageFormat_Ordinary", resourceCulture); + return ResourceManager.GetString("AssemblyInitializeShouldBeValidMessageFormat", resourceCulture); } } /// - /// Looks up a localized string similar to AssemblyInitialize method '{0}' should be 'public'. - /// - internal static string AssemblyInitializeShouldBeValidMessageFormat_Public { - get { - return ResourceManager.GetString("AssemblyInitializeShouldBeValidMessageFormat_Public", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask'. - /// - internal static string AssemblyInitializeShouldBeValidMessageFormat_ReturnType { - get { - return ResourceManager.GetString("AssemblyInitializeShouldBeValidMessageFormat_ReturnType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext'. + /// Looks up a localized string similar to AssemblyInitialize methods should have valid layout. /// - internal static string AssemblyInitializeShouldBeValidMessageFormat_SingleContextParameter { + internal static string AssemblyInitializeShouldBeValidTitle { get { - return ResourceManager.GetString("AssemblyInitializeShouldBeValidMessageFormat_SingleContextParameter", resourceCulture); + return ResourceManager.GetString("AssemblyInitializeShouldBeValidTitle", resourceCulture); } } /// - /// Looks up a localized string similar to AssemblyInitialize method '{0}' should be 'static'. + /// Looks up a localized string similar to Prefer adding an additional assertion that checks for null. /// - internal static string AssemblyInitializeShouldBeValidMessageFormat_Static { + internal static string AssertionArgsShouldAvoidConditionalAccessMessageFormat { get { - return ResourceManager.GetString("AssemblyInitializeShouldBeValidMessageFormat_Static", resourceCulture); + return ResourceManager.GetString("AssertionArgsShouldAvoidConditionalAccessMessageFormat", resourceCulture); } } /// - /// Looks up a localized string similar to AssemblyInitialize methods should have valid layout. + /// Looks up a localized string similar to Avoid conditional access in assertions. /// - internal static string AssemblyInitializeShouldBeValidTitle { + internal static string AssertionArgsShouldAvoidConditionalAccessTitle { get { - return ResourceManager.GetString("AssemblyInitializeShouldBeValidTitle", resourceCulture); + return ResourceManager.GetString("AssertionArgsShouldAvoidConditionalAccessTitle", resourceCulture); } } @@ -310,13 +202,13 @@ internal static string AvoidExpectedExceptionAttributeTitle { /// /// Looks up a localized string similar to Methods marked with [ClassCleanup] should follow the following layout to be valid: - ///- it should be 'public' - ///- it should not 'static' - ///- it should not be generic - ///- it should not take any parameter + ///- be 'public' + ///- not be 'static' + ///- not be generic nor defined on a generic class + ///- not take any parameter ///- return type should be 'void', 'Task' or 'ValueTask' - ///- it should not be 'async void' - ///- it should not be a special method (finalizer, operator...).. + ///- not be 'async void' + ///- not be a special method (finalizer, operator...).. /// internal static string ClassCleanupShouldBeValidDescription { get { @@ -325,74 +217,11 @@ internal static string ClassCleanupShouldBeValidDescription { } /// - /// Looks up a localized string similar to ClassCleanup method '{0}' should not take any parameter. + /// Looks up a localized string similar to ClassCleanup method '{0}' signature is invalid. /// - internal static string ClassCleanupShouldBeValidMessageFormat_NoParameters { + internal static string ClassCleanupShouldBeValidMessageFormat { get { - return ResourceManager.GetString("ClassCleanupShouldBeValidMessageFormat_NoParameters", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set. - /// - internal static string ClassCleanupShouldBeValidMessageFormat_NotAGenericClassUnlessInheritanceModeSet { - get { - return ResourceManager.GetString("ClassCleanupShouldBeValidMessageFormat_NotAGenericClassUnlessInheritanceModeSet", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask'. - /// - internal static string ClassCleanupShouldBeValidMessageFormat_NotAsyncVoid { - get { - return ResourceManager.GetString("ClassCleanupShouldBeValidMessageFormat_NotAsyncVoid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassCleanup method '{0}' should not be generic. - /// - internal static string ClassCleanupShouldBeValidMessageFormat_NotGeneric { - get { - return ResourceManager.GetString("ClassCleanupShouldBeValidMessageFormat_NotGeneric", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassCleanup method '{0}' should be an 'ordinary' method. - /// - internal static string ClassCleanupShouldBeValidMessageFormat_Ordinary { - get { - return ResourceManager.GetString("ClassCleanupShouldBeValidMessageFormat_Ordinary", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassCleanup method '{0}' should be 'public'. - /// - internal static string ClassCleanupShouldBeValidMessageFormat_Public { - get { - return ResourceManager.GetString("ClassCleanupShouldBeValidMessageFormat_Public", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask'. - /// - internal static string ClassCleanupShouldBeValidMessageFormat_ReturnType { - get { - return ResourceManager.GetString("ClassCleanupShouldBeValidMessageFormat_ReturnType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassCleanup method '{0}' should be 'static'. - /// - internal static string ClassCleanupShouldBeValidMessageFormat_Static { - get { - return ResourceManager.GetString("ClassCleanupShouldBeValidMessageFormat_Static", resourceCulture); + return ResourceManager.GetString("ClassCleanupShouldBeValidMessageFormat", resourceCulture); } } @@ -407,13 +236,13 @@ internal static string ClassCleanupShouldBeValidTitle { /// /// Looks up a localized string similar to Methods marked with [ClassInitialize] should follow the following layout to be valid: - ///- it should be 'public' - ///- it should be 'static' - ///- it should not be generic - ///- it should take one parameter of type 'TestContext' + ///- be 'public' + ///- be 'static' + ///- not be generic nor be defined on a generic class + ///- take a single parameter of type 'TestContext' ///- return type should be 'void', 'Task' or 'ValueTask' - ///- it should not be 'async void' - ///- it should not be a special method (finalizer, operator...).. + ///- not be 'async void' + ///- not be a special method (finalizer, operator...).. /// internal static string ClassInitializeShouldBeValidDescription { get { @@ -422,75 +251,11 @@ internal static string ClassInitializeShouldBeValidDescription { } /// - /// Looks up a localized string similar to ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set. - /// - internal static string ClassInitializeShouldBeValidMessageFormat_NotAGenericClassUnlessInheritanceModeSet { - get { - return ResourceManager.GetString("ClassInitializeShouldBeValidMessageFormat_NotAGenericClassUnlessInheritanceModeSe" + - "t", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask'. - /// - internal static string ClassInitializeShouldBeValidMessageFormat_NotAsyncVoid { - get { - return ResourceManager.GetString("ClassInitializeShouldBeValidMessageFormat_NotAsyncVoid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassInitialize method '{0}' should not be generic. - /// - internal static string ClassInitializeShouldBeValidMessageFormat_NotGeneric { - get { - return ResourceManager.GetString("ClassInitializeShouldBeValidMessageFormat_NotGeneric", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassInitialize method '{0}' should be an 'ordinary' method. - /// - internal static string ClassInitializeShouldBeValidMessageFormat_Ordinary { - get { - return ResourceManager.GetString("ClassInitializeShouldBeValidMessageFormat_Ordinary", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassInitialize method '{0}' should be 'public'. - /// - internal static string ClassInitializeShouldBeValidMessageFormat_Public { - get { - return ResourceManager.GetString("ClassInitializeShouldBeValidMessageFormat_Public", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask'. - /// - internal static string ClassInitializeShouldBeValidMessageFormat_ReturnType { - get { - return ResourceManager.GetString("ClassInitializeShouldBeValidMessageFormat_ReturnType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassInitialize method '{0}' should take a single parameter of type 'TestContext'. - /// - internal static string ClassInitializeShouldBeValidMessageFormat_SingleContextParameter { - get { - return ResourceManager.GetString("ClassInitializeShouldBeValidMessageFormat_SingleContextParameter", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ClassInitialize method '{0}' should be 'static'. + /// Looks up a localized string similar to ClassInitialize method '{0}' signature is invalid. /// - internal static string ClassInitializeShouldBeValidMessageFormat_Static { + internal static string ClassInitializeShouldBeValidMessageFormat { get { - return ResourceManager.GetString("ClassInitializeShouldBeValidMessageFormat_Static", resourceCulture); + return ResourceManager.GetString("ClassInitializeShouldBeValidMessageFormat", resourceCulture); } } @@ -587,6 +352,33 @@ internal static string DoNotStoreStaticTestContextAnalyzerTitle { } } + /// + /// Looks up a localized string similar to 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead.. + /// + internal static string DoNotUseSystemDescriptionAttributeDescription { + get { + return ResourceManager.GetString("DoNotUseSystemDescriptionAttributeDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'?. + /// + internal static string DoNotUseSystemDescriptionAttributeMessageFormat { + get { + return ResourceManager.GetString("DoNotUseSystemDescriptionAttributeMessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 'System.ComponentModel.DescriptionAttribute' has no effect on test methods. + /// + internal static string DoNotUseSystemDescriptionAttributeTitle { + get { + return ResourceManager.GetString("DoNotUseSystemDescriptionAttributeTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert. /// @@ -677,6 +469,33 @@ internal static string PreferTestInitializeOverConstructorTitle { } } + /// + /// Looks up a localized string similar to Public methods should be test methods (marked with `[TestMethod]`).. + /// + internal static string PublicMethodShouldBeTestMethodAnalyzerDescription { + get { + return ResourceManager.GetString("PublicMethodShouldBeTestMethodAnalyzerDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Public method '{0}' should be a test method. + /// + internal static string PublicMethodShouldBeTestMethodAnalyzerFormat { + get { + return ResourceManager.GetString("PublicMethodShouldBeTestMethodAnalyzerFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Public methods should be test methods. + /// + internal static string PublicMethodShouldBeTestMethodAnalyzerTitle { + get { + return ResourceManager.GetString("PublicMethodShouldBeTestMethodAnalyzerTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to It's considered a good practice to have only test classes marked public in a test project.. /// @@ -704,6 +523,24 @@ internal static string PublicTypeShouldBeTestClassTitle { } } + /// + /// Looks up a localized string similar to Review or remove the assertion as its condition is known to be always true. + /// + internal static string ReviewAlwaysTrueAssertConditionAnalyzerMessageFormat { + get { + return ResourceManager.GetString("ReviewAlwaysTrueAssertConditionAnalyzerMessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Assertion condition is always true. + /// + internal static string ReviewAlwaysTrueAssertConditionAnalyzerTitle { + get { + return ResourceManager.GetString("ReviewAlwaysTrueAssertConditionAnalyzerTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Test classes, classes marked with the '[TestClass]' attribute, should respect the following layout to be considered valid by MSTest: ///- it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) @@ -781,14 +618,14 @@ internal static string TestClassShouldHaveTestMethodTitle { /// /// Looks up a localized string similar to Methods marked with [TestCleanup] should follow the following layout to be valid: - ///- it should be 'public' - ///- it should not be 'static' - ///- it should not be generic - ///- it should not be 'abstract' - ///- it should not take any parameter + ///- be 'public' + ///- not be 'static' + ///- not be generic or be defined on a generic class + ///- not be 'abstract' + ///- not take any parameter ///- return type should be 'void', 'Task' or 'ValueTask' - ///- it should not be 'async void' - ///- it should not be a special method (finalizer, operator...).. + ///- not be 'async void' + ///- not be a special method (finalizer, operator...).. /// internal static string TestCleanupShouldBeValidDescription { get { @@ -797,74 +634,11 @@ internal static string TestCleanupShouldBeValidDescription { } /// - /// Looks up a localized string similar to TestCleanup method '{0}' should not take any parameter. - /// - internal static string TestCleanupShouldBeValidMessageFormat_NoParameters { - get { - return ResourceManager.GetString("TestCleanupShouldBeValidMessageFormat_NoParameters", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestCleanup method '{0}' should not be 'abstract'. - /// - internal static string TestCleanupShouldBeValidMessageFormat_NotAbstract { - get { - return ResourceManager.GetString("TestCleanupShouldBeValidMessageFormat_NotAbstract", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestCleanup method '{0}' should not be 'async void'. + /// Looks up a localized string similar to TestCleanup method '{0}' signature is invalid. /// - internal static string TestCleanupShouldBeValidMessageFormat_NotAsyncVoid { + internal static string TestCleanupShouldBeValidMessageFormat { get { - return ResourceManager.GetString("TestCleanupShouldBeValidMessageFormat_NotAsyncVoid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestCleanup method '{0}' should not be generic. - /// - internal static string TestCleanupShouldBeValidMessageFormat_NotGeneric { - get { - return ResourceManager.GetString("TestCleanupShouldBeValidMessageFormat_NotGeneric", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestCleanup method '{0}' should not be 'static'. - /// - internal static string TestCleanupShouldBeValidMessageFormat_NotStatic { - get { - return ResourceManager.GetString("TestCleanupShouldBeValidMessageFormat_NotStatic", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestCleanup method '{0}' should be an 'ordinary' method. - /// - internal static string TestCleanupShouldBeValidMessageFormat_Ordinary { - get { - return ResourceManager.GetString("TestCleanupShouldBeValidMessageFormat_Ordinary", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestCleanup method '{0}' should be 'public'. - /// - internal static string TestCleanupShouldBeValidMessageFormat_Public { - get { - return ResourceManager.GetString("TestCleanupShouldBeValidMessageFormat_Public", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask'. - /// - internal static string TestCleanupShouldBeValidMessageFormat_ReturnType { - get { - return ResourceManager.GetString("TestCleanupShouldBeValidMessageFormat_ReturnType", resourceCulture); + return ResourceManager.GetString("TestCleanupShouldBeValidMessageFormat", resourceCulture); } } @@ -946,14 +720,14 @@ internal static string TestContextShouldBeValidTitle { /// /// Looks up a localized string similar to Methods marked with [TestInitialize] should follow the following layout to be valid: - ///- it should be 'public' - ///- it should not be 'static' - ///- it should not be generic - ///- it should not be 'abstract' - ///- it should not take any parameter + ///- be 'public' + ///- not be 'static' + ///- not be generic nor defined on a generic class + ///- not be 'abstract' + ///- not take any parameter ///- return type should be 'void', 'Task' or 'ValueTask' - ///- it should not be 'async void' - ///- it should not be a special method (finalizer, operator...).. + ///- not be 'async void' + ///- not be a special method (finalizer, operator...).. /// internal static string TestInitializeShouldBeValidDescription { get { @@ -962,74 +736,11 @@ internal static string TestInitializeShouldBeValidDescription { } /// - /// Looks up a localized string similar to TestInitialize method '{0}' should not take any parameter. - /// - internal static string TestInitializeShouldBeValidMessageFormat_NoParameters { - get { - return ResourceManager.GetString("TestInitializeShouldBeValidMessageFormat_NoParameters", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestInitialize method '{0}' should not be 'abstract'. - /// - internal static string TestInitializeShouldBeValidMessageFormat_NotAbstract { - get { - return ResourceManager.GetString("TestInitializeShouldBeValidMessageFormat_NotAbstract", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestInitialize method '{0}' should not be 'async void'. - /// - internal static string TestInitializeShouldBeValidMessageFormat_NotAsyncVoid { - get { - return ResourceManager.GetString("TestInitializeShouldBeValidMessageFormat_NotAsyncVoid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestInitialize method '{0}' should not be generic. - /// - internal static string TestInitializeShouldBeValidMessageFormat_NotGeneric { - get { - return ResourceManager.GetString("TestInitializeShouldBeValidMessageFormat_NotGeneric", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestInitialize method '{0}' should not be 'static'. - /// - internal static string TestInitializeShouldBeValidMessageFormat_NotStatic { - get { - return ResourceManager.GetString("TestInitializeShouldBeValidMessageFormat_NotStatic", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestInitialize method '{0}' should be an 'ordinary' method. - /// - internal static string TestInitializeShouldBeValidMessageFormat_Ordinary { - get { - return ResourceManager.GetString("TestInitializeShouldBeValidMessageFormat_Ordinary", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestInitialize method '{0}' should be 'public'. - /// - internal static string TestInitializeShouldBeValidMessageFormat_Public { - get { - return ResourceManager.GetString("TestInitializeShouldBeValidMessageFormat_Public", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask'. + /// Looks up a localized string similar to TestInitialize method '{0}' signature is invalid. /// - internal static string TestInitializeShouldBeValidMessageFormat_ReturnType { + internal static string TestInitializeShouldBeValidMessageFormat { get { - return ResourceManager.GetString("TestInitializeShouldBeValidMessageFormat_ReturnType", resourceCulture); + return ResourceManager.GetString("TestInitializeShouldBeValidMessageFormat", resourceCulture); } } @@ -1166,6 +877,51 @@ internal static string TestMethodShouldNotBeIgnoredAnalyzerTitle { } } + /// + /// Looks up a localized string similar to Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored.. + /// + internal static string TypeContainingTestMethodShouldBeATestClassDescription { + get { + return ResourceManager.GetString("TypeContainingTestMethodShouldBeATestClassDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Class '{0}' contains test methods and should be marked with '[TestClass]'. + /// + internal static string TypeContainingTestMethodShouldBeATestClassMessageFormat { + get { + return ResourceManager.GetString("TypeContainingTestMethodShouldBeATestClassMessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type containing '[TestMethod]' should be marked with '[TestClass]'. + /// + internal static string TypeContainingTestMethodShouldBeATestClassTitle { + get { + return ResourceManager.GetString("TypeContainingTestMethodShouldBeATestClassTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Asynchronous test fixture methods do not require the 'Async' suffix. + /// + internal static string UseAsyncSuffixTestFixtureMethodSuppressorJustification { + get { + return ResourceManager.GetString("UseAsyncSuffixTestFixtureMethodSuppressorJustification", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Asynchronous test methods do not require the 'Async' suffix. + /// + internal static string UseAsyncSuffixTestMethodSuppressorJustification { + get { + return ResourceManager.GetString("UseAsyncSuffixTestMethodSuppressorJustification", resourceCulture); + } + } + /// /// Looks up a localized string similar to [{0}] can only be set on methods marked with [TestMethod]. /// diff --git a/src/Analyzers/MSTest.Analyzers/Resources.resx b/src/Analyzers/MSTest.Analyzers/Resources.resx index 17607ff84d..df12f9b58a 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.resx +++ b/src/Analyzers/MSTest.Analyzers/Resources.resx @@ -119,78 +119,42 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - - - AssemblyCleanup method '{0}' should not take any parameter - - - AssemblyCleanup method '{0}' can't be declared on a generic class - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - - - AssemblyCleanup method '{0}' should not be generic - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - - - AssemblyCleanup method '{0}' should be 'public' - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' +- not be 'async void' +- not be a special method (finalizer, operator...). - - AssemblyCleanup method '{0}' should be 'static' + + AssemblyCleanup method '{0}' signature is invalid AssemblyCleanup methods should have valid layout Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - - - AssemblyInitialize method '{0}' can't be declared on a generic class - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - - - AssemblyInitialize method '{0}' should not be generic - - - AssemblyInitialize method '{0}' should be an 'ordinary' method - - - AssemblyInitialize method '{0}' should be 'public' - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' +- not be 'async void' +- not be a special method (finalizer, operator...). - - AssemblyInitialize method '{0}' should be 'static' + + AssemblyInitialize method '{0}' signature is invalid AssemblyInitialize methods should have valid layout + + Prefer adding an additional assertion that checks for null + + + Avoid conditional access in assertions + 'Assert.AreEqual', 'Assert.AreNotEqual', 'Assert.AreSame' and 'Assert.AreNotSame' expects the expected value to be passed first and the actual value to be passed as second argument. @@ -211,74 +175,32 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - - - ClassCleanup method '{0}' should not take any parameter - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - - - ClassCleanup method '{0}' should not be generic - - - ClassCleanup method '{0}' should be an 'ordinary' method +- not be 'async void' +- not be a special method (finalizer, operator...). - - ClassCleanup method '{0}' should be 'public' - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - - - ClassCleanup method '{0}' should be 'static' + + ClassCleanup method '{0}' signature is invalid ClassCleanup methods should have valid layout Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - - - ClassInitialize method '{0}' should not be generic - - - ClassInitialize method '{0}' should be an 'ordinary' method - - - ClassInitialize method '{0}' should be 'public' +- not be 'async void' +- not be a special method (finalizer, operator...). - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - - - ClassInitialize method '{0}' should be 'static' + + ClassInitialize method '{0}' signature is invalid ClassInitialize methods should have valid layout @@ -343,6 +265,15 @@ Prefer TestInitialize methods over constructors + + Public methods should be test methods (marked with `[TestMethod]`). + + + Public method '{0}' should be a test method + + + Public methods should be test methods + It's considered a good practice to have only test classes marked public in a test project. @@ -381,38 +312,17 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). - - TestCleanup method '{0}' should not take any parameter - - - TestCleanup method '{0}' should not be 'abstract' - - - TestCleanup method '{0}' should not be 'async void' - - - TestCleanup method '{0}' should not be generic - - - TestCleanup method '{0}' should not be 'static' - - - TestCleanup method '{0}' should be an 'ordinary' method - - - TestCleanup method '{0}' should be 'public' - - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' + + TestCleanup method '{0}' signature is invalid TestCleanup method should have valid layout @@ -444,38 +354,17 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - - - TestInitialize method '{0}' should not take any parameter - - - TestInitialize method '{0}' should not be 'abstract' - - - TestInitialize method '{0}' should not be 'async void' +- not be 'async void' +- not be a special method (finalizer, operator...). - - TestInitialize method '{0}' should not be generic - - - TestInitialize method '{0}' should not be 'static' - - - TestInitialize method '{0}' should be an 'ordinary' method - - - TestInitialize method '{0}' should be 'public' - - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' + + TestInitialize method '{0}' signature is invalid TestInitialize method should have valid layout @@ -526,6 +415,12 @@ Test method should not be ignored + + Asynchronous test fixture methods do not require the 'Async' suffix + + + Asynchronous test methods do not require the 'Async' suffix + [{0}] can only be set on methods marked with [TestMethod] @@ -541,4 +436,28 @@ Explicitly enable or disable tests parallelization + + Type containing '[TestMethod]' should be marked with '[TestClass]' + + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + + + Assertion condition is always true + + + Review or remove the assertion as its condition is known to be always true + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/ReviewAlwaysTrueAssertConditionAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/ReviewAlwaysTrueAssertConditionAnalyzer.cs new file mode 100644 index 0000000000..662877724d --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/ReviewAlwaysTrueAssertConditionAnalyzer.cs @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +using MSTest.Analyzers.Helpers; +using MSTest.Analyzers.RoslynAnalyzerHelpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0032: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class ReviewAlwaysTrueAssertConditionAnalyzer : DiagnosticAnalyzer +{ + private enum EqualityStatus + { + Unknown, + Equal, + NotEqual, + } + + private const string ExpectedParameterName = "expected"; + private const string NotExpectedParameterName = "notExpected"; + private const string ActualParameterName = "actual"; + private const string ConditionParameterName = "condition"; + private const string ValueParameterName = "value"; + + private static readonly LocalizableResourceString Title = new(nameof(Resources.ReviewAlwaysTrueAssertConditionAnalyzerTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.ReviewAlwaysTrueAssertConditionAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.ReviewAlwaysTrueAssertConditionAnalyzerRuleId, + Title, + MessageFormat, + null, + Category.Design, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction(context => + { + Compilation compilation = context.Compilation; + INamedTypeSymbol? assertSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssert); + INamedTypeSymbol? nullableSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemNullable); + if (assertSymbol is not null) + { + context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol, nullableSymbol), OperationKind.Invocation); + } + }); + } + + private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol, INamedTypeSymbol? nullableSymbol) + { + var operation = (IInvocationOperation)context.Operation; + if (assertSymbol.Equals(operation.TargetMethod.ContainingType, SymbolEqualityComparer.Default) && + IsAlwaysTrue(operation, nullableSymbol)) + { + context.ReportDiagnostic(operation.CreateDiagnostic(Rule)); + } + } + + private static bool IsAlwaysTrue(IInvocationOperation operation, INamedTypeSymbol? nullableSymbol) + => operation.TargetMethod.Name switch + { + "IsTrue" => GetConditionArgument(operation) is { Value.ConstantValue: { HasValue: true, Value: true } }, + "IsFalse" => GetConditionArgument(operation) is { Value.ConstantValue: { HasValue: true, Value: false } }, + "AreEqual" => GetEqualityStatus(operation, ExpectedParameterName) == EqualityStatus.Equal, + "AreNotEqual" => GetEqualityStatus(operation, NotExpectedParameterName) == EqualityStatus.NotEqual, + "IsNull" => GetValueArgument(operation) is { Value.ConstantValue: { HasValue: true, Value: null } }, + "IsNotNull" => GetValueArgument(operation) is { } valueArgumentOperation && IsNotNullableType(valueArgumentOperation, nullableSymbol), + _ => false, + }; + + private static bool IsNotNullableType(IArgumentOperation valueArgumentOperation, INamedTypeSymbol? nullableSymbol) + { + ITypeSymbol? valueArgType = valueArgumentOperation.Value.GetReferencedMemberOrLocalOrParameter().GetReferencedMemberOrLocalOrParameter(); + return valueArgType is not null + && valueArgType.NullableAnnotation == NullableAnnotation.NotAnnotated + && !SymbolEqualityComparer.IncludeNullability.Equals(valueArgType.OriginalDefinition, nullableSymbol); + } + + private static IArgumentOperation? GetArgumentWithName(IInvocationOperation operation, string name) + => operation.Arguments.FirstOrDefault(arg => arg.Parameter?.Name == name); + + private static IArgumentOperation? GetConditionArgument(IInvocationOperation operation) + => GetArgumentWithName(operation, ConditionParameterName); + + private static IArgumentOperation? GetValueArgument(IInvocationOperation operation) + => GetArgumentWithName(operation, ValueParameterName); + + private static EqualityStatus GetEqualityStatus(IInvocationOperation operation, string expectedOrNotExpectedParameterName) + { + if (GetArgumentWithName(operation, expectedOrNotExpectedParameterName) is { } expectedOrNotExpectedArgument && + GetArgumentWithName(operation, ActualParameterName) is { } actualArgument && + expectedOrNotExpectedArgument.Value.ConstantValue.HasValue && + actualArgument.Value.ConstantValue.HasValue) + { + return Equals(expectedOrNotExpectedArgument.Value.ConstantValue.Value, actualArgument.Value.ConstantValue.Value) ? EqualityStatus.Equal : EqualityStatus.NotEqual; + } + + // We are not sure about the equality status + return EqualityStatus.Unknown; + } +} diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs index 18ef7d5999..70cd5b5113 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs @@ -12,14 +12,14 @@ internal class BoundedCacheWithFactory { // Bounded weak reference cache. // Size 5 is an arbitrarily chosen bound, which can be tuned in future as required. - private readonly List> _weakReferencedEntries = new() - { - new WeakReference(null), + private readonly List> _weakReferencedEntries = + [ new WeakReference(null), new WeakReference(null), new WeakReference(null), new WeakReference(null), - }; + new WeakReference(null) + ]; public TValue GetOrCreateValue(TKey key, Func valueFactory) { diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/DiagnosticExtensions.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/DiagnosticExtensions.cs index b3b3dbe380..be456a6521 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/DiagnosticExtensions.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/DiagnosticExtensions.cs @@ -8,7 +8,7 @@ namespace Analyzer.Utilities.Extensions { - internal static class DiagnosticExtensions + internal static class FixtureUtils { public static Diagnostic CreateDiagnostic( this SyntaxNode node, @@ -207,11 +207,11 @@ public static void ReportNoLocationDiagnostic( object?[] parameters; if (syntaxTreeOptionsProviderTryGetDiagnosticValueMethod.GetParameters().Length == 3) { - parameters = new object?[] { tree, rule.Id, null }; + parameters = [tree, rule.Id, null]; } else { - parameters = new object?[] { tree, rule.Id, CancellationToken.None, null }; + parameters = [tree, rule.Id, CancellationToken.None, null]; } if (syntaxTreeOptionsProviderTryGetDiagnosticValueMethod.Invoke(syntaxTreeOptionsProvider, parameters) is true && diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/WellKnownTypeProvider.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/WellKnownTypeProvider.cs index 71cbc2f86a..b994f452f5 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/WellKnownTypeProvider.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/WellKnownTypeProvider.cs @@ -26,7 +26,7 @@ public class WellKnownTypeProvider private WellKnownTypeProvider(Compilation compilation) { Compilation = compilation; - _fullNameToTypeMap = new ConcurrentDictionary(StringComparer.Ordinal); + _fullNameToTypeMap = new ConcurrentDictionary(); _referencedAssemblies = new Lazy>( () => Compilation.Assembly.Modules .SelectMany(m => m.ReferencedAssemblySymbols) @@ -60,21 +60,19 @@ public static WellKnownTypeProvider GetOrCreate(Compilation compilation) /// private readonly ConcurrentDictionary _fullNameToTypeMap; -#if !NETSTANDARD1_3 // Assuming we're on .NET Standard 2.0 or later, cache the type names that are probably compile time constants. /// /// Static cache of full type names (with namespaces) to namespace name parts, /// so we can query . /// /// + /// /// Example: "System.Collections.Generic.List`1" => [ "System", "Collections", "Generic" ] /// /// https://github.com/dotnet/roslyn/blob/9e786147b8cb884af454db081bb747a5bd36a086/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs#L455 /// suggests the TypeNames collection can be checked to avoid expensive operations. But realizing TypeNames seems to be /// as memory intensive as unnecessary calls GetTypeByMetadataName() in some cases. So we'll go with namespace names. /// - private static readonly ConcurrentDictionary> _fullTypeNameToNamespaceNames = - new(StringComparer.Ordinal); -#endif + private static readonly ConcurrentDictionary> _fullTypeNameToNamespaceNames = new(); /// /// Attempts to get the type by the full type name. @@ -112,9 +110,7 @@ private bool TryGetOrCreateTypeByMetadataNameSlow( INamedTypeSymbol? type = null; ImmutableArray namespaceNames; -#if NETSTANDARD1_3 // Probably in 2.9.x branch; just don't cache. - namespaceNames = GetNamespaceNamesFromFullTypeName(fullTypeName); -#else // Assuming we're on .NET Standard 2.0 or later, cache the type names that are probably compile time constants. + // Assuming we're on .NET Standard 2.0 or later, cache the type names that are probably compile time constants. if (string.IsInterned(fullTypeName) != null) { namespaceNames = _fullTypeNameToNamespaceNames.GetOrAdd( @@ -125,7 +121,6 @@ private bool TryGetOrCreateTypeByMetadataNameSlow( { namespaceNames = GetNamespaceNamesFromFullTypeName(fullTypeName); } -#endif if (IsSubsetOfCollection(namespaceNames, Compilation.Assembly.NamespaceNames)) { @@ -261,12 +256,6 @@ private static bool IsIdentifierPartCharacter(char ch) UnicodeCategory cat = CharUnicodeInfo.GetUnicodeCategory(ch); - ////return IsLetterChar(cat) - //// || IsDecimalDigitChar(cat) - //// || IsConnectingChar(cat) - //// || IsCombiningChar(cat) - //// || IsFormattingChar(cat); - return cat switch { // Letter diff --git a/src/Analyzers/MSTest.Analyzers/TestClassShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestClassShouldBeValidAnalyzer.cs index b03d4feeb5..6b1c146d20 100644 --- a/src/Analyzers/MSTest.Analyzers/TestClassShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestClassShouldBeValidAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0002: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class TestClassShouldBeValidAnalyzer : DiagnosticAnalyzer { diff --git a/src/Analyzers/MSTest.Analyzers/TestClassShouldHaveTestMethodAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestClassShouldHaveTestMethodAnalyzer.cs index c8a27326a6..3f8b36c49a 100644 --- a/src/Analyzers/MSTest.Analyzers/TestClassShouldHaveTestMethodAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestClassShouldHaveTestMethodAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0016: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class TestClassShouldHaveTestMethodAnalyzer : DiagnosticAnalyzer { diff --git a/src/Analyzers/MSTest.Analyzers/TestCleanupShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestCleanupShouldBeValidAnalyzer.cs index cd21ce8595..8c3c74b4e9 100644 --- a/src/Analyzers/MSTest.Analyzers/TestCleanupShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestCleanupShouldBeValidAnalyzer.cs @@ -12,32 +12,22 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0009: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class TestCleanupShouldBeValidAnalyzer : DiagnosticAnalyzer { - private static readonly LocalizableResourceString Title = new(nameof(Resources.TestCleanupShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableResourceString Description = new(nameof(Resources.TestCleanupShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.TestCleanupShouldBeValidMessageFormat_Public), Resources.ResourceManager, typeof(Resources)); - - internal static readonly DiagnosticDescriptor PublicRule = DiagnosticDescriptorHelper.Create( + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( DiagnosticIds.TestCleanupShouldBeValidRuleId, - Title, - MessageFormat, - Description, + new LocalizableResourceString(nameof(Resources.TestCleanupShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.TestCleanupShouldBeValidMessageFormat), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.TestCleanupShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)), Category.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true); - internal static readonly DiagnosticDescriptor NotStaticRule = PublicRule.WithMessage(new(nameof(Resources.TestCleanupShouldBeValidMessageFormat_NotStatic), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NoParametersRule = PublicRule.WithMessage(new(nameof(Resources.TestCleanupShouldBeValidMessageFormat_NoParameters), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor ReturnTypeRule = PublicRule.WithMessage(new(nameof(Resources.TestCleanupShouldBeValidMessageFormat_ReturnType), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotAsyncVoidRule = PublicRule.WithMessage(new(nameof(Resources.TestCleanupShouldBeValidMessageFormat_NotAsyncVoid), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotAbstractRule = PublicRule.WithMessage(new(nameof(Resources.TestCleanupShouldBeValidMessageFormat_NotAbstract), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotGenericRule = PublicRule.WithMessage(new(nameof(Resources.TestCleanupShouldBeValidMessageFormat_NotGeneric), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor OrdinaryRule = PublicRule.WithMessage(new(nameof(Resources.TestCleanupShouldBeValidMessageFormat_Ordinary), Resources.ResourceManager, typeof(Resources))); - - public override ImmutableArray SupportedDiagnostics { get; } - = ImmutableArray.Create(PublicRule); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { @@ -46,70 +36,30 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { - if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestCleanupAttribute, out INamedTypeSymbol? testCleanupAttributeSymbol)) + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestCleanupAttribute, out INamedTypeSymbol? testCleanupAttributeSymbol) + && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute, out INamedTypeSymbol? testClassAttributeSymbol)) { INamedTypeSymbol? taskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); INamedTypeSymbol? valueTaskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); bool canDiscoverInternals = context.Compilation.CanDiscoverInternals(); context.RegisterSymbolAction( - context => AnalyzeSymbol(context, testCleanupAttributeSymbol, taskSymbol, valueTaskSymbol, canDiscoverInternals), + context => AnalyzeSymbol(context, testCleanupAttributeSymbol, taskSymbol, valueTaskSymbol, testClassAttributeSymbol, canDiscoverInternals), SymbolKind.Method); } }); } private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testCleanupAttributeSymbol, INamedTypeSymbol? taskSymbol, - INamedTypeSymbol? valueTaskSymbol, bool canDiscoverInternals) + INamedTypeSymbol? valueTaskSymbol, INamedTypeSymbol testClassAttributeSymbol, bool canDiscoverInternals) { var methodSymbol = (IMethodSymbol)context.Symbol; - if (!methodSymbol.IsTestCleanupMethod(testCleanupAttributeSymbol)) - { - return; - } - - if (methodSymbol.MethodKind != MethodKind.Ordinary) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(OrdinaryRule, methodSymbol.Name)); - - // Do not check the other criteria, users should fix the method kind first. - return; - } - - if (methodSymbol.IsAbstract) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAbstractRule, methodSymbol.Name)); - } - - if (methodSymbol.IsGenericMethod) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotGenericRule, methodSymbol.Name)); - } - - if (methodSymbol.Parameters.Length > 0) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NoParametersRule, methodSymbol.Name)); - } - - if (methodSymbol.IsStatic) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotStaticRule, methodSymbol.Name)); - } - - if (methodSymbol.ReturnsVoid && methodSymbol.IsAsync) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAsyncVoidRule, methodSymbol.Name)); - } - - if (!methodSymbol.IsPublicAndHasCorrectResultantVisibility(canDiscoverInternals)) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(PublicRule, methodSymbol.Name)); - } - - if (!methodSymbol.ReturnsVoid - && (taskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, taskSymbol)) - && (valueTaskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, valueTaskSymbol))) + if (methodSymbol.IsTestCleanupMethod(testCleanupAttributeSymbol) + && !methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: false, + allowGenericType: false, testContextSymbol: null, testClassAttributeSymbol, fixtureAllowInheritedTestClass: true, out bool isFixable)) { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(ReturnTypeRule, methodSymbol.Name)); + context.ReportDiagnostic(isFixable + ? methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name) + : methodSymbol.CreateDiagnostic(Rule, DiagnosticDescriptorHelper.CannotFixProperties, methodSymbol.Name)); } } } diff --git a/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs index 94107b3b74..3e1163b3d4 100644 --- a/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0005: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class TestContextShouldBeValidAnalyzer : DiagnosticAnalyzer { diff --git a/src/Analyzers/MSTest.Analyzers/TestInitializeShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestInitializeShouldBeValidAnalyzer.cs index c7bdf3139f..ccecb936af 100644 --- a/src/Analyzers/MSTest.Analyzers/TestInitializeShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestInitializeShouldBeValidAnalyzer.cs @@ -12,32 +12,22 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0008: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class TestInitializeShouldBeValidAnalyzer : DiagnosticAnalyzer { - private static readonly LocalizableResourceString Title = new(nameof(Resources.TestInitializeShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableResourceString Description = new(nameof(Resources.TestInitializeShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.TestInitializeShouldBeValidMessageFormat_Public), Resources.ResourceManager, typeof(Resources)); - - internal static readonly DiagnosticDescriptor PublicRule = DiagnosticDescriptorHelper.Create( + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( DiagnosticIds.TestInitializeShouldBeValidRuleId, - Title, - MessageFormat, - Description, + new LocalizableResourceString(nameof(Resources.TestInitializeShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.TestInitializeShouldBeValidMessageFormat), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.TestInitializeShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)), Category.Usage, DiagnosticSeverity.Warning, isEnabledByDefault: true); - internal static readonly DiagnosticDescriptor NotStaticRule = PublicRule.WithMessage(new(nameof(Resources.TestInitializeShouldBeValidMessageFormat_NotStatic), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NoParametersRule = PublicRule.WithMessage(new(nameof(Resources.TestInitializeShouldBeValidMessageFormat_NoParameters), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor ReturnTypeRule = PublicRule.WithMessage(new(nameof(Resources.TestInitializeShouldBeValidMessageFormat_ReturnType), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotAsyncVoidRule = PublicRule.WithMessage(new(nameof(Resources.TestInitializeShouldBeValidMessageFormat_NotAsyncVoid), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotAbstractRule = PublicRule.WithMessage(new(nameof(Resources.TestInitializeShouldBeValidMessageFormat_NotAbstract), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor NotGenericRule = PublicRule.WithMessage(new(nameof(Resources.TestInitializeShouldBeValidMessageFormat_NotGeneric), Resources.ResourceManager, typeof(Resources))); - internal static readonly DiagnosticDescriptor OrdinaryRule = PublicRule.WithMessage(new(nameof(Resources.TestInitializeShouldBeValidMessageFormat_Ordinary), Resources.ResourceManager, typeof(Resources))); - - public override ImmutableArray SupportedDiagnostics { get; } - = ImmutableArray.Create(PublicRule); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { @@ -46,70 +36,30 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { - if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestInitializeAttribute, out INamedTypeSymbol? testInitializeAttributeSymbol)) + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestInitializeAttribute, out INamedTypeSymbol? testInitializeAttributeSymbol) + && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute, out INamedTypeSymbol? testClassAttributeSymbol)) { INamedTypeSymbol? taskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); INamedTypeSymbol? valueTaskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); bool canDiscoverInternals = context.Compilation.CanDiscoverInternals(); context.RegisterSymbolAction( - context => AnalyzeSymbol(context, testInitializeAttributeSymbol, taskSymbol, valueTaskSymbol, canDiscoverInternals), + context => AnalyzeSymbol(context, testInitializeAttributeSymbol, taskSymbol, valueTaskSymbol, testClassAttributeSymbol, canDiscoverInternals), SymbolKind.Method); } }); } private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testInitializeAttributeSymbol, INamedTypeSymbol? taskSymbol, - INamedTypeSymbol? valueTaskSymbol, bool canDiscoverInternals) + INamedTypeSymbol? valueTaskSymbol, INamedTypeSymbol testClassAttributeSymbol, bool canDiscoverInternals) { var methodSymbol = (IMethodSymbol)context.Symbol; - if (!methodSymbol.IsTestInitializeMethod(testInitializeAttributeSymbol)) - { - return; - } - - if (methodSymbol.MethodKind != MethodKind.Ordinary) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(OrdinaryRule, methodSymbol.Name)); - - // Do not check the other criteria, users should fix the method kind first. - return; - } - - if (methodSymbol.IsAbstract) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAbstractRule, methodSymbol.Name)); - } - - if (methodSymbol.IsGenericMethod) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotGenericRule, methodSymbol.Name)); - } - - if (methodSymbol.Parameters.Length > 0) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NoParametersRule, methodSymbol.Name)); - } - - if (methodSymbol.IsStatic) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotStaticRule, methodSymbol.Name)); - } - - if (methodSymbol.ReturnsVoid && methodSymbol.IsAsync) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAsyncVoidRule, methodSymbol.Name)); - } - - if (!methodSymbol.IsPublicAndHasCorrectResultantVisibility(canDiscoverInternals)) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(PublicRule, methodSymbol.Name)); - } - - if (!methodSymbol.ReturnsVoid - && (taskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, taskSymbol)) - && (valueTaskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, valueTaskSymbol))) + if (methodSymbol.IsTestInitializeMethod(testInitializeAttributeSymbol) + && !methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: false, + allowGenericType: false, testContextSymbol: null, testClassAttributeSymbol, fixtureAllowInheritedTestClass: true, out bool isFixable)) { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(ReturnTypeRule, methodSymbol.Name)); + context.ReportDiagnostic(isFixable + ? methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name) + : methodSymbol.CreateDiagnostic(Rule, DiagnosticDescriptorHelper.CannotFixProperties, methodSymbol.Name)); } } } diff --git a/src/Analyzers/MSTest.Analyzers/TestMethodShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestMethodShouldBeValidAnalyzer.cs index a6ede1d760..fb4cd2e5fe 100644 --- a/src/Analyzers/MSTest.Analyzers/TestMethodShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestMethodShouldBeValidAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0003: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class TestMethodShouldBeValidAnalyzer : DiagnosticAnalyzer { @@ -102,7 +105,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo } } - if (methodSymbol.ReturnsVoid && methodSymbol.IsAsync) + if (methodSymbol is { ReturnsVoid: true, IsAsync: true }) { context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotAsyncVoidRule, methodSymbol.Name)); } diff --git a/src/Analyzers/MSTest.Analyzers/TestMethodShouldNotBeIgnoredAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestMethodShouldNotBeIgnoredAnalyzer.cs index 82505600fd..c1bd4ae1fd 100644 --- a/src/Analyzers/MSTest.Analyzers/TestMethodShouldNotBeIgnoredAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestMethodShouldNotBeIgnoredAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0015: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class TestMethodShouldNotBeIgnoredAnalyzer : DiagnosticAnalyzer { diff --git a/src/Analyzers/MSTest.Analyzers/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs new file mode 100644 index 0000000000..f89aa288f8 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0030: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class TypeContainingTestMethodShouldBeATestClassAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.TypeContainingTestMethodShouldBeATestClassTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.TypeContainingTestMethodShouldBeATestClassDescription), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.TypeContainingTestMethodShouldBeATestClassMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor TypeContainingTestMethodShouldBeATestClassRule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.TypeContainingTestMethodShouldBeATestClassRuleId, + Title, + MessageFormat, + Description, + Category.Usage, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(TypeContainingTestMethodShouldBeATestClassRule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestMethodAttribute, out INamedTypeSymbol? testMethodAttributeSymbol) + && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute, out INamedTypeSymbol? testClassAttributeSymbol)) + { + context.RegisterSymbolAction( + context => AnalyzeSymbol(context, testClassAttributeSymbol, testMethodAttributeSymbol), + SymbolKind.NamedType); + } + }); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testClassAttributeSymbol, INamedTypeSymbol testMethodAttributeSymbol) + { + var namedTypeSymbol = (INamedTypeSymbol)context.Symbol; + if (namedTypeSymbol.TypeKind != TypeKind.Class + || namedTypeSymbol.IsAbstract) + { + return; + } + + bool isTestClass = false; + foreach (AttributeData classAttribute in namedTypeSymbol.GetAttributes()) + { + if (classAttribute.AttributeClass.Inherits(testClassAttributeSymbol)) + { + isTestClass = true; + break; + } + } + + if (isTestClass) + { + return; + } + + bool hasTestMethod = false; + INamedTypeSymbol? currentType = namedTypeSymbol; + do + { + foreach (ISymbol classMember in currentType.GetMembers()) + { + if (classMember.Kind != SymbolKind.Method) + { + continue; + } + + foreach (AttributeData attribute in classMember.GetAttributes()) + { + if (attribute.AttributeClass.Inherits(testMethodAttributeSymbol)) + { + hasTestMethod = true; + break; + } + } + + if (!hasTestMethod) + { + break; + } + } + + currentType = currentType.BaseType; + } + while (currentType is not null && !hasTestMethod); + + if (!hasTestMethod) + { + return; + } + + context.ReportDiagnostic(namedTypeSymbol.CreateDiagnostic(TypeContainingTestMethodShouldBeATestClassRule, namedTypeSymbol.Name)); + } +} diff --git a/src/Analyzers/MSTest.Analyzers/UseAsyncSuffixTestFixtureMethodSuppressor.cs b/src/Analyzers/MSTest.Analyzers/UseAsyncSuffixTestFixtureMethodSuppressor.cs new file mode 100644 index 0000000000..9ad047e912 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/UseAsyncSuffixTestFixtureMethodSuppressor.cs @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0028: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class UseAsyncSuffixTestFixtureMethodSuppressor : DiagnosticSuppressor +{ + // VSTHRD200: Use Async suffix for async methods + // https://github.com/microsoft/vs-threading/blob/main/doc/analyzers/VSTHRD200.md + private const string SuppressedDiagnosticId = "VSTHRD200"; + + internal static readonly SuppressionDescriptor Rule = + new(DiagnosticIds.UseAsyncSuffixTestFixtureMethodSuppressorRuleId, SuppressedDiagnosticId, Resources.UseAsyncSuffixTestFixtureMethodSuppressorJustification); + + public override ImmutableArray SupportedSuppressions { get; } = ImmutableArray.Create(Rule); + + public override void ReportSuppressions(SuppressionAnalysisContext context) + { + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssemblyInitializeAttribute, out INamedTypeSymbol? assemblyInitializeAttributeSymbol) + || !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssemblyCleanupAttribute, out INamedTypeSymbol? assemblyCleanupAttributeSymbol) + || !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassInitializeAttribute, out INamedTypeSymbol? classInitializeAttributeSymbol) + || !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassCleanupAttribute, out INamedTypeSymbol? classCleanupAttributeSymbol) + || !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestInitializeAttribute, out INamedTypeSymbol? testInitializeAttributeSymbol) + || !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestCleanupAttribute, out INamedTypeSymbol? testCleanupAttributeSymbol)) + { + return; + } + + foreach (Diagnostic diagnostic in context.ReportedDiagnostics) + { + // The diagnostic is reported on the test method + if (diagnostic.Location.SourceTree is not { } tree) + { + continue; + } + + SyntaxNode root = tree.GetRoot(context.CancellationToken); + SyntaxNode node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + + SemanticModel semanticModel = context.GetSemanticModel(tree); + ISymbol? declaredSymbol = semanticModel.GetDeclaredSymbol(node, context.CancellationToken); + if (declaredSymbol is IMethodSymbol method + && method.GetAttributes().Any(attr => + SymbolEqualityComparer.Default.Equals(attr.AttributeClass, assemblyInitializeAttributeSymbol) + || SymbolEqualityComparer.Default.Equals(attr.AttributeClass, assemblyCleanupAttributeSymbol) + || SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classInitializeAttributeSymbol) + || SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classCleanupAttributeSymbol) + || SymbolEqualityComparer.Default.Equals(attr.AttributeClass, testInitializeAttributeSymbol) + || SymbolEqualityComparer.Default.Equals(attr.AttributeClass, testCleanupAttributeSymbol))) + { + context.ReportSuppression(Suppression.Create(Rule, diagnostic)); + } + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/UseAsyncSuffixTestMethodSuppressor.cs b/src/Analyzers/MSTest.Analyzers/UseAsyncSuffixTestMethodSuppressor.cs new file mode 100644 index 0000000000..048c1a8bc0 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/UseAsyncSuffixTestMethodSuppressor.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0027: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class UseAsyncSuffixTestMethodSuppressor : DiagnosticSuppressor +{ + // VSTHRD200: Use Async suffix for async methods + // https://github.com/microsoft/vs-threading/blob/main/doc/analyzers/VSTHRD200.md + private const string SuppressedDiagnosticId = "VSTHRD200"; + + internal static readonly SuppressionDescriptor Rule = + new(DiagnosticIds.UseAsyncSuffixTestMethodSuppressorRuleId, SuppressedDiagnosticId, Resources.UseAsyncSuffixTestMethodSuppressorJustification); + + public override ImmutableArray SupportedSuppressions { get; } = ImmutableArray.Create(Rule); + + public override void ReportSuppressions(SuppressionAnalysisContext context) + { + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestMethodAttribute, out INamedTypeSymbol? testMethodAttributeSymbol)) + { + return; + } + + foreach (Diagnostic diagnostic in context.ReportedDiagnostics) + { + // The diagnostic is reported on the test method + if (diagnostic.Location.SourceTree is not { } tree) + { + continue; + } + + SyntaxNode root = tree.GetRoot(context.CancellationToken); + SyntaxNode node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + + SemanticModel semanticModel = context.GetSemanticModel(tree); + ISymbol? declaredSymbol = semanticModel.GetDeclaredSymbol(node, context.CancellationToken); + if (declaredSymbol is IMethodSymbol method + && method.GetAttributes().Any(attr => attr.AttributeClass.Inherits(testMethodAttributeSymbol))) + { + context.ReportSuppression(Suppression.Create(Rule, diagnostic)); + } + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/UseAttributeOnTestMethodAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseAttributeOnTestMethodAnalyzer.cs index 1fc2dfbf73..95a2f17e09 100644 --- a/src/Analyzers/MSTest.Analyzers/UseAttributeOnTestMethodAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseAttributeOnTestMethodAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0007: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class UseAttributeOnTestMethodAnalyzer : DiagnosticAnalyzer { @@ -112,8 +115,8 @@ public sealed class UseAttributeOnTestMethodAnalyzer : DiagnosticAnalyzer isEnabledByDefault: true); // IMPORTANT: Remember to add any new rule to the rule tuple. - private static readonly List<(string AttributeFullyQualifiedName, DiagnosticDescriptor Rule)> RuleTuples = new() - { + private static readonly List<(string AttributeFullyQualifiedName, DiagnosticDescriptor Rule)> RuleTuples = + [ (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingOwnerAttribute, OwnerRule), (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingPriorityAttribute, PriorityRule), (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestPropertyAttribute, TestPropertyRule), @@ -121,8 +124,8 @@ public sealed class UseAttributeOnTestMethodAnalyzer : DiagnosticAnalyzer (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingDescriptionAttribute, DescriptionRule), (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingExpectedExceptionAttribute, ExpectedExceptionRule), (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingCssIterationAttribute, CssIterationRule), - (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingCssProjectStructureAttribute, CssProjectStructureRule), - }; + (WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingCssProjectStructureAttribute, CssProjectStructureRule) + ]; public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(OwnerRule); @@ -146,7 +149,7 @@ public override void Initialize(AnalysisContext context) // Get a list of attributes and associated rules that are found in the current compilation // context. - List<(INamedTypeSymbol AttributeSymbol, DiagnosticDescriptor Rule)> attributeRuleTuples = new(); + List<(INamedTypeSymbol AttributeSymbol, DiagnosticDescriptor Rule)> attributeRuleTuples = []; foreach ((string attributeFullyQualifiedName, DiagnosticDescriptor rule) in RuleTuples) { if (context.Compilation.TryGetOrCreateTypeByMetadataName(attributeFullyQualifiedName, out INamedTypeSymbol? attributeSymbol)) @@ -172,7 +175,7 @@ private static void AnalyzeSymbol( { var methodSymbol = (IMethodSymbol)context.Symbol; - List<(AttributeData AttributeData, DiagnosticDescriptor Rule)> attributes = new(); + List<(AttributeData AttributeData, DiagnosticDescriptor Rule)> attributes = []; foreach (AttributeData methodAttribute in methodSymbol.GetAttributes()) { // Current method should be a test method or should inherit from the TestMethod attribute. diff --git a/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs index fc43cc423c..b3295b921e 100644 --- a/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0001: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class UseParallelizeAttributeAnalyzer : DiagnosticAnalyzer { diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf index d38f1a1d56..870e1193e1 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf @@ -4,62 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Aby byly metody s označením [AssemblyCleanup] platné, musí se řídit následujícím rozložením: -. -– musí být public, -– musí být static, -– nesmí být obecné, -– nesmí přijímat žádný parametr, +– být „public“ +– být „static“ +– nesmí být obecné ani definované pro obecnou třídu +– nesmí přijímat žádné parametry – návratový typ musí být void, Task nebo ValueTask, -– nesmí být async void, -– nesmí být speciální metodou (finalizační metoda, operátor...). +– nesmí být „async void“ +– nesmí se jednat o speciální metodu (finalizační metoda, operátor...). - - AssemblyCleanup method '{0}' should not take any parameter - Metoda AssemblyCleanup {0} by neměla přijímat žádný parametr. - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - Metodu AssemblyCleanup {0} nelze deklarovat pro obecnou třídu. - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda AssemblyCleanup {0} by měla vracet hodnoty void, Task nebo ValueTask. - - - - AssemblyCleanup method '{0}' should not be generic - Metoda AssemblyCleanup {0} by neměla být obecná. - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - Metoda AssemblyCleanup {0} by měla být metoda ordinary. - - - - AssemblyCleanup method '{0}' should be 'public' - Metoda AssemblyCleanup {0} by měla být public. - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda AssemblyCleanup {0} by měla vracet hodnoty void, Task nebo ValueTask. - - - - AssemblyCleanup method '{0}' should be 'static' - Metoda AssemblyCleanup {0} by neměla být static. + + AssemblyCleanup method '{0}' signature is invalid + Signatura „{0}“ metody AssemblyCleanup je neplatná @@ -69,67 +33,42 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Aby byly metody s označením [AssemblyInitialize] platné, musí se řídit následujícím rozložením: . -– musí být public, -– musí být static, -– nesmí být obecné, -– musí mít jeden parametr typu TestContext, +– být „public“ +– být „static“ +– nesmí být obecné ani definované pro obecnou třídu +– převzít jeden parametr typu TestContext – návratový typ musí být void, Task nebo ValueTask, -– nesmí být async void, -– nesmí být speciální metodou (finalizační metoda, operátor...). - - - - AssemblyInitialize method '{0}' can't be declared on a generic class - Metodu AssemblyInitialize {0} nelze deklarovat pro obecnou třídu. +– nesmí být „async void“ +– nesmí se jednat o speciální metodu (finalizační metoda, operátor...). - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda AssemblyInitialize {0} by měla vracet hodnoty void, Task nebo ValueTask. + + AssemblyInitialize method '{0}' signature is invalid + Signatura „{0}“ metody AssemblyInitialize je neplatná - - AssemblyInitialize method '{0}' should not be generic - Metoda AssemblyInitialize {0} by neměla být obecná. - - - - AssemblyInitialize method '{0}' should be an 'ordinary' method - Metoda AssemblyInitialize {0} by měla být metoda ordinary. - - - - AssemblyInitialize method '{0}' should be 'public' - Metoda AssemblyInitialize {0} by měla být public. - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda AssemblyInitialize {0} by měla vracet hodnoty void, Task nebo ValueTask. - - - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - Metoda AssemblyInitialize {0} by měla mít jeden parametr typu TestContext. + + AssemblyInitialize methods should have valid layout + Metody AssemblyInitialize musí mít platné rozložení - - AssemblyInitialize method '{0}' should be 'static' - Metoda AssemblyInitialize {0} by měla být static. + + Prefer adding an additional assertion that checks for null + Preferovat přidání dalšího kontrolního výrazu, který kontroluje hodnotu null - - AssemblyInitialize methods should have valid layout - Metody AssemblyInitialize musí mít platné rozložení + + Avoid conditional access in assertions + Vyhnout se podmíněnému přístupu v kontrolních výrazech @@ -164,62 +103,27 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Aby byly metody s označením [ClassCleanup] platné, musí se řídit následujícím rozložením: . -– musí být public, -– nesmí být static, -– nesmí být obecné, -– nesmí přijímat žádný parametr, +– být „public“ +– nesmí být „static“ +– nesmí být obecné ani definované pro obecnou třídu +– nesmí přijímat žádné parametry – návratový typ musí být void, Task nebo ValueTask, -– nesmí být async void, -– nesmí být speciální metodou (finalizační metoda, operátor...). - - - - ClassCleanup method '{0}' should not take any parameter - Metoda ClassCleanup {0} by neměla přijímat žádný parametr. - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - Metodu ClassCleanup {0} nelze deklarovat pro obecnou třídu bez nastavení režimu InheritanceBehavior. - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda ClassCleanup {0} by měla vracet hodnoty void, Task nebo ValueTask. - - - - ClassCleanup method '{0}' should not be generic - Metoda ClassCleanup {0} by neměla být obecná. - - - - ClassCleanup method '{0}' should be an 'ordinary' method - Metoda ClassCleanup {0} by měla být metoda ordinary. +– nesmí být „async void“ +– nesmí se jednat o speciální metodu (finalizační metoda, operátor...). - - ClassCleanup method '{0}' should be 'public' - Metoda ClassCleanup {0} by měla být public. - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda ClassCleanup {0} by měla vracet hodnoty void, Task nebo ValueTask. - - - - ClassCleanup method '{0}' should be 'static' - Metoda ClassCleanup {0} by měla být static. + + ClassCleanup method '{0}' signature is invalid + Signatura „{0}“ metody ClassCleanup je neplatná @@ -229,62 +133,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Aby byly metody s označením [ClassInitialize] platné, musí se řídit následujícím rozložením: -. -– musí být public, -– musí být static, -– nesmí být obecné, -– musí mít jeden parametr typu TestContext, +– být „public“ +– být „static“ +– nesmí být obecné ani definované pro obecnou třídu +– převzít jeden parametr typu TestContext – návratový typ musí být void, Task nebo ValueTask, -– nesmí být async void, -– nesmí být speciální metodou (finalizační metoda, operátor...). - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - Metodu ClassInitialize {0} nelze deklarovat pro obecnou třídu bez nastavení režimu InheritanceBehavior. - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda ClassInitialize {0} by měla vracet hodnoty void, Task nebo ValueTask. - - - - ClassInitialize method '{0}' should not be generic - Metoda ClassInitialize {0} by neměla být obecná. - - - - ClassInitialize method '{0}' should be an 'ordinary' method - Metoda ClassInitialize {0} by měla být metoda ordinary. +– nesmí být „async void“ +– nesmí se jednat o speciální metodu (finalizační metoda, operátor...). - - ClassInitialize method '{0}' should be 'public' - Metoda ClassInitialize {0} by měla být public. - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda ClassInitialize {0} by měla vracet hodnoty void, Task nebo ValueTask. - - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - Metoda ClassInitialize {0} by měla mít jeden parametr typu TestContext. - - - - ClassInitialize method '{0}' should be 'static' - Metoda ClassInitialize {0} by měla být static. + + ClassInitialize method '{0}' signature is invalid + Signatura „{0}“ metody ClassInitialize je neplatná @@ -343,6 +211,21 @@ Neukládejte TestContext ve statickém členu + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert Místo trvalého neúspěšného vyhodnocovacího výrazu „Assert.{0}“ použijte „Assert.Fail“. @@ -353,6 +236,16 @@ Místo trvalého neúspěšného vyhodnocovacího výrazu použijte „Assert.Fail“. + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods Upřednostňovat konstruktory před metodami TestInitialize @@ -393,6 +286,21 @@ Upřednostňovat metody TestInitialize před konstruktory + + Public methods should be test methods (marked with `[TestMethod]`). + Veřejné metody by měly být testovací metody (označené jako [TestMethod]). + + + + Public method '{0}' should be a test method + Veřejná metoda {0} by měla být testovací metoda. + + + + Public methods should be test methods + Veřejné metody by měly být testovací metody + + It's considered a good practice to have only test classes marked public in a test project. Osvědčeným postupem je označit jako veřejné v testovacím projektu jen testovací třídy. @@ -456,64 +364,29 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Aby byly metody s označením [TestCleanup] platné, musí se řídit následujícím rozložením: . -– musí být public, -– nesmí být static, -– nesmí být obecné, -– nesmí být abstract, -– nesmí přijímat žádný parametr, -– návratový typ musí být void, Task nebo ValueTask, -– nesmí být async void, -– nesmí být speciální metodou (finalizační metoda, operátor...). - - - - TestCleanup method '{0}' should not take any parameter - Metoda TestCleanup {0} by neměla přijímat žádný parametr. - - - - TestCleanup method '{0}' should not be 'abstract' - Metoda TestCleanup {0} by neměla být abstract. - - - - TestCleanup method '{0}' should not be 'async void' - Metoda TestCleanup {0} by neměla být async void. - - - - TestCleanup method '{0}' should not be generic - Metoda TestCleanup {0} by neměla být obecná. +– být „public“ +– nesmí být „static“ +– nesmí být obecné nebo definované pro obecnou třídu +– nesmí být „abstract“ +– nesmí přijímat žádné parametry +– návratový typ musí být „void“, „Task“ nebo „ValueTask“ +– nesmí být „async void“ +– nesmí se jednat o speciální metodu (finalizační metoda, operátor...). - - TestCleanup method '{0}' should not be 'static' - Metoda TestCleanup {0} by neměla být static. - - - - TestCleanup method '{0}' should be an 'ordinary' method - Metoda TestCleanup {0} by měla být metoda ordinary. - - - - TestCleanup method '{0}' should be 'public' - Metoda TestCleanup {0} by měla být public. - - - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda TestCleanup {0} by měla vracet hodnoty void, Task nebo ValueTask. + + TestCleanup method '{0}' signature is invalid + Signatura „{0}“ metody TestCleanup je neplatná @@ -566,64 +439,29 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Aby byly metody s označením [TestInitialize] platné, musí se řídit následujícím rozložením: . -– musí být public, -– nesmí být static, -– nesmí být obecné, -– nesmí být abstract, -– nesmí přijímat žádný parametr, -– návratový typ musí být void, Task nebo ValueTask, -– nesmí být async void, -– nesmí být speciální metodou (finalizační metoda, operátor...). - - - - TestInitialize method '{0}' should not take any parameter - Metoda TestInitialize {0} by neměla přijímat žádný parametr. - - - - TestInitialize method '{0}' should not be 'abstract' - Metoda TestInitialize {0} by neměla být abstract. +– být „public“ +– nesmí být „static“ +– nesmí být obecné ani definované pro obecnou třídu +– nesmí být „abstract“ +– nesmí přijímat žádné parametry +– návratový typ musí být „void“, „Task“ nebo „ValueTask“ +– nesmí být „async void“ +– nesmí se jednat o speciální metodu (finalizační metoda, operátor...). - - TestInitialize method '{0}' should not be 'async void' - Metoda TestInitialize {0} by neměla být async void. - - - - TestInitialize method '{0}' should not be generic - Metoda TestInitialize {0} by neměla být obecná. - - - - TestInitialize method '{0}' should not be 'static' - Metoda TestInitialize {0} by neměla být static. - - - - TestInitialize method '{0}' should be an 'ordinary' method - Metoda TestInitialize {0} by měla být metoda ordinary. - - - - TestInitialize method '{0}' should be 'public' - Metoda TestInitialize {0} by neměla být public. - - - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda TestInitialize {0} by měla vracet hodnoty void, Task nebo ValueTask. + + TestInitialize method '{0}' signature is invalid + Signatura „{0}“ metody TestInitialize je neplatná @@ -710,6 +548,31 @@ Testovací metoda by se neměla ignorovat. + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + Class '{0}' contains test methods and should be marked with '[TestClass]' + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + Type containing '[TestMethod]' should be marked with '[TestClass]' + + + + Asynchronous test fixture methods do not require the 'Async' suffix + Asynchronní metody testovacích přípravků nevyžadují příponu Async. + + + + Asynchronous test methods do not require the 'Async' suffix + Asynchronní testovací metody nevyžadují příponu Async. + + [{0}] can only be set on methods marked with [TestMethod] [{0}] lze nastavit pouze u metod označených pomocí metody [TestMethod]. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf index caab1e4786..d8d8b2cbb3 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf @@ -4,61 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Methoden, die mit [AssemblyCleanup] gekennzeichnet sind, müssen dem folgenden Layout folgen, um gültig zu sein: -– Muss „public“ sein -– Muss „static“ sein -– Darf nicht „generic“ sein -– Darf keinen Parameter annehmen -– Der Rückgabetyp muss „void“, „Task“ oder „ValueTask“ sein. -– Darf nicht „async void“ sein -– Darf keine spezielle Methode sein (Finalizer, Operator...) - - - - AssemblyCleanup method '{0}' should not take any parameter - Die AssemblyCleanup-Methode „{0}“ darf keinen Parameter annehmen. - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - Die AssemblyCleanup-Methode „{0}“ kann nicht in einer generischen Klasse deklariert werden. - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Die AssemblyCleanup-Methode „{0}“ muss „void“, „Task“ oder „ValueTask“ zurückgeben. - - - - AssemblyCleanup method '{0}' should not be generic - Die AssemblyCleanup-Methode „{0}“ darf nicht generisch sein. - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - Die AssemblyCleanup-Methode „{0}“ muss eine „ordinary“ Methode sein. - - - - AssemblyCleanup method '{0}' should be 'public' - Die AssemblyCleanup-Methode „{0}“ muss „public“ sein. - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Die AssemblyCleanup-Methode „{0}“ muss „void“, „Task“ oder „ValueTask“ zurückgeben. +– "öffentlich" sein +– "statisch" sein +– nicht generisch oder für eine generische Klasse definiert sein +– keinen Parameter annehmen +– der Rückgabetyp muss "void", "Task" oder "ValueTask" sein +– nicht "async void" sein +– keine spezielle Methode sein (Finalizer, Operator...) - - AssemblyCleanup method '{0}' should be 'static' - Die AssemblyCleanup-Methode „{0}“ muss „static“ sein. + + AssemblyCleanup method '{0}' signature is invalid + Signatur der AssemblyCleanup-Methode "{0}" ist ungültig @@ -68,66 +33,41 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Methoden, die mit [AssemblyInitialize] gekennzeichnet sind, müssen dem folgenden Layout folgen, um gültig zu sein: -– Muss „public“ sein -– Muss „static“ sein -– Darf nicht „generic“ sein -– Muss einen Parameter vom Typ „TestContext“ annehmen -– Der Rückgabetyp muss „void“, „Task“ oder „ValueTask“ sein. -– Darf nicht „async void“ sein -– Darf keine spezielle Methode sein (Finalizer, Operator...) - - - - AssemblyInitialize method '{0}' can't be declared on a generic class - Die AssemblyInitialize-Methode „{0}“ kann nicht in einer generischen Klasse deklariert werden. - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Die AssemblyInitialize-Methode „{0}“ muss „void“, „Task“ oder „ValueTask“ zurückgeben. - - - - AssemblyInitialize method '{0}' should not be generic - Die AssemblyInitialize-Methode „{0}“ darf nicht „generic“ sein. - - - - AssemblyInitialize method '{0}' should be an 'ordinary' method - Die AssemblyInitialize-Methode „{0}“ muss eine „ordinary“-Methode sein. - - - - AssemblyInitialize method '{0}' should be 'public' - Die AssemblyInitialize-Methode „{0}“ muss „public“ sein. +– "öffentlich" sein +– "statisch" sein +– nicht generisch oder für eine generische Klasse definiert sein +– einen einfachen Parameter vom Typ „TestContext“ annehmen +– der Rückgabetyp muss "void", "Task" oder "ValueTask" sein +– nicht "async void" sein +– keine spezielle Methode sein (Finalizer, Operator...) - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Die AssemblyInitialize-Methode „{0}“ muss „void“, „Task“ oder „ValueTask“ zurückgeben. + + AssemblyInitialize method '{0}' signature is invalid + Signatur der AssemblyInitialize-Methode "{0}" ist ungültig - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - Die AssemblyInitialize-Methode „{0}“ muss einen einzelnen Parameter vom Typ „TestContext“ annehmen. + + AssemblyInitialize methods should have valid layout + AssemblyInitialize-Methoden müssen über ein gültiges Layout verfügen. - - AssemblyInitialize method '{0}' should be 'static' - Die AssemblyInitialize-Methode „{0}“ muss „static“ sein. + + Prefer adding an additional assertion that checks for null + Lieber eine zusätzliche Assertion hinzufügen, die auf NULL überprüft - - AssemblyInitialize methods should have valid layout - AssemblyInitialize-Methoden müssen über ein gültiges Layout verfügen. + + Avoid conditional access in assertions + Bedingten Zugriff in Assertionen vermeiden @@ -162,61 +102,26 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Methoden, die mit [ClassCleanup] gekennzeichnet sind, müssen dem folgenden Layout folgen, um gültig zu sein: -– Muss „public“ sein -– Darf nicht „static“ sein -– Darf nicht „generic“ sein -– Darf keinen Parameter annehmen -– Der Rückgabetyp muss „void“, „Task“ oder „ValueTask“ sein. -– Darf nicht „async void“ sein -– Darf keine spezielle Methode sein (Finalizer, Operator...) - - - - ClassCleanup method '{0}' should not take any parameter - Die ClassCleanup-Methode „{0}“ darf keinen Parameter annehmen. - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - Die ClassCleanup-Methode „{0}“ kann sich nicht in einer generischen Klasse deklariert werden, ohne dass der InheritanceBehavior-Modus festgelegt ist. - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Die ClassCleanup-Methode „{0}“ muss „void“, „Task“ oder „ValueTask“ zurückgeben. - - - - ClassCleanup method '{0}' should not be generic - Die ClassCleanup-Methode „{0}“ darf nicht generisch sein. - - - - ClassCleanup method '{0}' should be an 'ordinary' method - Die ClassCleanup-Methode „{0}“ muss eine „ordinary“ Methode sein. - - - - ClassCleanup method '{0}' should be 'public' - Die ClassCleanup-Methode „{0}“ muss „public“ sein. - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Die ClassCleanup-Methode „{0}“ muss „void“, „Task“ oder „ValueTask“ zurückgeben. +– "öffentlich" sein +– nicht "statisch" sein +– nicht generisch oder für eine generische Klasse definiert sein +– keinen Parameter annehmen +– der Rückgabetyp muss "void", "Task" oder "ValueTask" sein +– nicht "async void" sein +– keine spezielle Methode sein (Finalizer, Operator...) - - ClassCleanup method '{0}' should be 'static' - Die ClassCleanup-Methode „{0}“ muss „static“ sein. + + ClassCleanup method '{0}' signature is invalid + Signatur der ClassCleanup-Methode "{0}" ist ungültig @@ -226,61 +131,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Methoden, die mit [ClassInitialize] gekennzeichnet sind, müssen dem folgenden Layout folgen, um gültig zu sein: -– Muss „public“ sein -– Muss „static“ sein -– Darf nicht „generic“ sein -– Muss einen Parameter vom Typ „TestContext“ annehmen -– Der Rückgabetyp muss „void“, „Task“ oder „ValueTask“ sein. -– Darf nicht „async void“ sein -– Darf keine spezielle Methode sein (Finalizer, Operator...) - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - Die ClassInitialize-Methode „{0}“ kann sich nicht in einer generischen Klasse deklariert werden, ohne dass der InheritanceBehavior-Modus festgelegt ist. - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Die ClassInitialize-Methode „{0}“ muss „void“, „Task“ oder „ValueTask“ zurückgeben. - - - - ClassInitialize method '{0}' should not be generic - Die ClassInitialize-Methode „{0}“ darf nicht „generic“ sein. - - - - ClassInitialize method '{0}' should be an 'ordinary' method - Die ClassInitialize-Methode „{0}“ muss eine „ordinary“-Methode sein. - - - - ClassInitialize method '{0}' should be 'public' - Die ClassInitialize-Methode „{0}“ muss „public“ sein. - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Die ClassInitialize-Methode „{0}“ muss „void“, „Task“ oder „ValueTask“ zurückgeben. - - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - Die ClassInitialize-Methode „{0}“ muss einen einzelnen Parameter vom Typ „TestContext“ annehmen. +– "öffentlich" sein +– "statisch" sein +– nicht generisch oder für eine generische Klasse definiert sein +– einen einfachen Parameter vom Typ „TestContext“ annehmen +– der Rückgabetyp muss "void", "Task" oder "ValueTask" sein +– nicht "async void" sein +– keine spezielle Methode sein (Finalizer, Operator...) - - ClassInitialize method '{0}' should be 'static' - Die ClassInitialize-Methode „{0}“ muss „static“ sein. + + ClassInitialize method '{0}' signature is invalid + Signatur der ClassInitialize-Methode "{0}" ist ungültig @@ -339,6 +209,21 @@ TestContext nicht in einem statischen Member speichern + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert Verwenden Sie „Assert.Fail“ anstelle einer Assert-Anweisung „Assert.{0}“, bei der immer ein Fehler auftritt. @@ -349,6 +234,16 @@ Verwenden Sie „Assert.Fail“ anstelle einer Assert-Anweisung, bei der immer ein Fehler auftritt. + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods Konstruktoren gegenüber TestInitialize-Methoden bevorzugen @@ -389,6 +284,21 @@ TestInitialize-Methoden gegenüber Konstruktoren bevorzugen + + Public methods should be test methods (marked with `[TestMethod]`). + Public methods should be test methods (marked with `[TestMethod]`). + + + + Public method '{0}' should be a test method + Public method '{0}' should be a test method + + + + Public methods should be test methods + Public methods should be test methods + + It's considered a good practice to have only test classes marked public in a test project. Es wird als bewährte Methode angesehen, nur Testklassen in einem Testprojekt als öffentlich gekennzeichnet zu lassen. @@ -452,63 +362,28 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Methoden, die mit [TestCleanup] gekennzeichnet sind, sollten dem folgenden Layout folgen, um gültig zu sein: -– es sollte "public" sein -– es darf nicht "static" sein -– es darf nicht "generic" sein -– es darf nicht "abstract" sein -– es sollte keinen Parameter verwenden +– "öffentlich" sein +– nicht "statisch" sein +– nicht generisch oder für eine generische Klasse definiert sein +– nicht "abstrakt" sein +– keinen Parameter annehmen – der Rückgabetyp muss "void", "Task" oder "ValueTask" sein -– es darf nicht "async void" sein -– es darf keine spezielle Methode (Finalizer, Operator...) sein. - - - - TestCleanup method '{0}' should not take any parameter - Die TestCleanup-Methode "{0}" darf keinen Parameter annehmen. - - - - TestCleanup method '{0}' should not be 'abstract' - Die TestCleanup-Methode "{0}" darf nicht "abstract" sein. - - - - TestCleanup method '{0}' should not be 'async void' - Die TestCleanup-Methode "{0}" darf nicht "sync void" sein. - - - - TestCleanup method '{0}' should not be generic - Die TestCleanup-Methode "{0}" darf nicht generisch sein. - - - - TestCleanup method '{0}' should not be 'static' - Die TestCleanup-Methode "{0}" darf nicht "static" sein. - - - - TestCleanup method '{0}' should be an 'ordinary' method - Die TestCleanup-Methode "{0}" darf eine "ordinary" Methode sein. - - - - TestCleanup method '{0}' should be 'public' - Die TestCleanup-Methode "{0}" muss "public" sein. +– nicht "async void" sein +– keine spezielle Methode sein (Finalizer, Operator...) - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Die TestCleanup-Methode "{0}" sollte "void", "Task" oder "ValueTask" zurückgeben. + + TestCleanup method '{0}' signature is invalid + Signatur der TestCleanup-Methode "{0}" ist ungültig @@ -561,63 +436,28 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Methoden, die mit [TestInitialize] gekennzeichnet sind, sollten dem folgenden Layout folgen, um gültig zu sein: -– es sollte "public" sein -– es darf nicht "static" sein -– es darf nicht "generic" sein -– es darf nicht "abstract" sein -– es sollte keinen Parameter verwenden +– "öffentlich" sein +– nicht "statisch" sein +– nicht generisch oder für eine generische Klasse definiert sein +– nicht "abstrakt" sein +– keinen Parameter annehmen – der Rückgabetyp muss "void", "Task" oder "ValueTask" sein -– es darf nicht "async void" sein -– es darf keine spezielle Methode (Finalizer, Operator...) sein. +– nicht "async void" sein +– keine spezielle Methode sein (Finalizer, Operator...) - - TestInitialize method '{0}' should not take any parameter - Die TestInitialize-Methode "{0}" darf keinen Parameter annehmen. - - - - TestInitialize method '{0}' should not be 'abstract' - Die TestInitialize-Methode "{0}" darf nicht "abstract" sein. - - - - TestInitialize method '{0}' should not be 'async void' - Die TestInitialize-Methode "{0}" darf nicht "async void" sein. - - - - TestInitialize method '{0}' should not be generic - Die TestInitialize-Methode "{0}" darf nicht "generic" sein. - - - - TestInitialize method '{0}' should not be 'static' - Die TestInitialize-Methode "{0}" darf nicht "static" sein. - - - - TestInitialize method '{0}' should be an 'ordinary' method - Die TestInitialize-Methode "{0}" darf eine "ordinary" Methode sein. - - - - TestInitialize method '{0}' should be 'public' - Die TestInitialize-Methode "{0}" sollte "public" sein. - - - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Die TestInitialize-Methode "{0}" sollte "void", "Task" oder "ValueTask" zurückgeben. + + TestInitialize method '{0}' signature is invalid + Signatur der TestInitialize-Methode "{0}" ist ungültig @@ -704,6 +544,31 @@ Die Testmethode darf nicht ignoriert werden. + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + Class '{0}' contains test methods and should be marked with '[TestClass]' + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + Type containing '[TestMethod]' should be marked with '[TestClass]' + + + + Asynchronous test fixture methods do not require the 'Async' suffix + Für asynchrone Testfixture-Methoden ist das Suffix "Async" nicht erforderlich. + + + + Asynchronous test methods do not require the 'Async' suffix + Für asynchrone Testmethoden ist das Suffix "Async" nicht erforderlich. + + [{0}] can only be set on methods marked with [TestMethod] [{0}] kann nur für Methoden festgelegt werden, die mit [TestMethod] markiert sind. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf index 6548d1cb5b..53663d480b 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf @@ -4,61 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Los métodos marcados con [AssemblyCleanup] deben seguir el siguiente diseño para ser válidos: -- debería ser 'público' -- debería estar 'estático' -- no debería ser genérico -- no debería tomar ningún parámetro -- el tipo de valor devuelto debería ser 'void', 'Task' o 'ValueTask' -- no debería ser 'async void' -- no debería ser un método especial (finalizador, operador...). - - - - AssemblyCleanup method '{0}' should not take any parameter - El método AssemblyCleanup '{0}' no debería tomar ningún parámetro - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - El método AssemblyCleanup "{0}" no puede ser declarado en una clase genérica - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - El método AssemblyCleanup '{0}' debería devolver 'void', 'Task' o 'ValueTask' - - - - AssemblyCleanup method '{0}' should not be generic - El método AssemblyCleanup '{0}' no debería ser genérico - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - El método AssemblyCleanup '{0}' debería ser un método 'normal' - - - - AssemblyCleanup method '{0}' should be 'public' - El método AssemblyCleanup '{0}' debería ser 'público' - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - El método AssemblyCleanup '{0}' debería devolver 'void', 'Task' o 'ValueTask' +- ser "public" +- ser "static" +- no ser genérico ni estar definido en una clase genérica +- no tomar ningún parámetro +- el tipo de valor devuelto debería ser "void", "Task" o "ValueTask" +- no ser "async void" +- no ser un método especial (finalizador, operador...). - - AssemblyCleanup method '{0}' should be 'static' - El método AssemblyCleanup '{0}' debería ser 'estático' + + AssemblyCleanup method '{0}' signature is invalid + La signatura "{0}" del método AssemblyCleanup no es válida @@ -68,66 +33,41 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Los métodos marcados con [AssemblyInitialize] deberían seguir el siguiente diseño para ser válidos: -- debería ser 'público' -- debería estar 'estático' -- no debería ser genérico -- debería tomar un parámetro de tipo 'TestContext' -- el tipo de valor devuelto debería ser 'void', 'Task' o 'ValueTask' -- no debería ser 'async void' -- no debería ser un método especial (finalizador, operador...). - - - - AssemblyInitialize method '{0}' can't be declared on a generic class - El método AssemblyInitialize "{0}" no puede declararse en una clase genérica - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - El método AssemblyInitialize '{0}' debería devolver 'void', 'Task' o 'ValueTask' - - - - AssemblyInitialize method '{0}' should not be generic - El método AssemblyInitialize '{0}' no debería ser genérico +- ser "public" +- ser "static" +- no ser genérico ni estar definido en una clase genérica +- tome un único parámetro de tipo "TestContext". +- el tipo de valor devuelto debería ser "void", "Task" o "ValueTask" +- no ser "async void" +- no ser un método especial (finalizador, operador...). - - AssemblyInitialize method '{0}' should be an 'ordinary' method - El método AssemblyInitialize '{0}' debería ser un método 'ordinario' + + AssemblyInitialize method '{0}' signature is invalid + La signatura "{0}" del método AssemblyInitialize no es válida - - AssemblyInitialize method '{0}' should be 'public' - El método AssemblyInitialize '{0}' debería ser 'público' - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - El método AssemblyInitialize '{0}' debería devolver 'void', 'Task' o 'ValueTask' + + AssemblyInitialize methods should have valid layout + Los métodos AssemblyInitialize deben tener un diseño válido - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - El método AssemblyInitialize '{0}' debería tomar un único parámetro de tipo 'TestContext' + + Prefer adding an additional assertion that checks for null + Preferir agregar una aserción adicional que compruebe si hay valores NULL - - AssemblyInitialize method '{0}' should be 'static' - El método AssemblyInitialize '{0}' debería ser 'estático' - - - - AssemblyInitialize methods should have valid layout - Los métodos AssemblyInitialize deben tener un diseño válido + + Avoid conditional access in assertions + Evitar el acceso condicional en las aserciones @@ -162,61 +102,26 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Los métodos marcados con [ClassCleanup] deben seguir el siguiente diseño para ser válidos: -- debería ser 'público' -- no debería ser 'estático' -- no debería ser genérico -- no debería tomar ningún parámetro -- el tipo de valor devuelto debería ser 'void', 'Task' o 'ValueTask' -- no debería ser 'async void' -- no debería ser un método especial (finalizador, operador...). - - - - ClassCleanup method '{0}' should not take any parameter - El método ClassCleanup '{0}' no debería tomar ningún parámetro - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - El método ClassCleanup "{0}" no puede ser declarado en una clase genérica sin que el modo "InheritanceBehavior" esté configurado. - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - El método ClassCleanup '{0}' debería devolver 'void', 'Task' o 'ValueTask' - - - - ClassCleanup method '{0}' should not be generic - El método ClassCleanup '{0}' no debería ser genérico - - - - ClassCleanup method '{0}' should be an 'ordinary' method - El método ClassCleanup '{0}' debería ser un método 'normal' - - - - ClassCleanup method '{0}' should be 'public' - El método ClassCleanup '{0}' debería ser 'público' +- ser "public" +- no ser "static" +- no ser genérico ni estar definido en una clase genérica +- no tomar ningún parámetro +- el tipo de valor devuelto debería ser "void", "Task" o "ValueTask" +- no ser "async void" +- no ser un método especial (finalizador, operador...). - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - El método ClassCleanup '{0}' debería devolver 'void', 'Task' o 'ValueTask' - - - - ClassCleanup method '{0}' should be 'static' - El método ClassCleanup '{0}' debería ser 'estático' + + ClassCleanup method '{0}' signature is invalid + La signatura "{0}" del método ClassCleanup no es válida @@ -226,61 +131,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Los métodos marcados con [ClassInitialize] deberían seguir el siguiente diseño para ser válidos: -- debería ser 'público' -- debería estar 'estático' -- no debería ser genérico -- debería tomar un parámetro de tipo 'TestContext' -- el tipo de valor devuelto debería ser 'void', 'Task' o 'ValueTask' -- no debería ser 'async void' -- no debería ser un método especial (finalizador, operador...). - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - El método ClassInitialize "{0}" no puede ser declarado en una clase genérica sin que el modo "InheritanceBehavior" esté configurado. - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - El método ClassInitialize '{0}' debería devolver 'void', 'Task' o 'ValueTask' - - - - ClassInitialize method '{0}' should not be generic - El método ClassInitialize '{0}' no debería ser genérico - - - - ClassInitialize method '{0}' should be an 'ordinary' method - El método ClassInitialize '{0}' debería ser un método 'ordinario' - - - - ClassInitialize method '{0}' should be 'public' - El método ClassInitialize '{0}' debería ser 'público' - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - El método ClassInitialize '{0}' debería devolver 'void', 'Task' o 'ValueTask' - - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - El método ClassInitialize '{0}' debe tomar un único parámetro de tipo 'TestContext' +- ser "public" +- ser "static" +- no ser genérico ni estar definido en una clase genérica +- tome un único parámetro de tipo "TestContext". +- el tipo de valor devuelto debería ser "void", "Task" o "ValueTask" +- no ser "async void" +- no ser un método especial (finalizador, operador...). - - ClassInitialize method '{0}' should be 'static' - El método ClassInitialize '{0}' debería ser 'estático' + + ClassInitialize method '{0}' signature is invalid + La signatura "{0}" del método ClassInitialize no es válida @@ -339,6 +209,21 @@ No almacenar TestContext en un miembro estático + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert Usar "Assert.Fail" en lugar de una aserción 'Assert.{0}' que siempre tiene errores @@ -349,6 +234,16 @@ Usar "Assert.Fail" en lugar de una aserción que siempre tiene errores + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods Preferir constructores en lugar de métodos TestInitialize @@ -389,6 +284,21 @@ Preferir métodos TestInitialize en lugar de constructores + + Public methods should be test methods (marked with `[TestMethod]`). + Public methods should be test methods (marked with `[TestMethod]`). + + + + Public method '{0}' should be a test method + Public method '{0}' should be a test method + + + + Public methods should be test methods + Public methods should be test methods + + It's considered a good practice to have only test classes marked public in a test project. Se considera una buena práctica tener solo clases de prueba marcadas como públicas en un proyecto de prueba. @@ -452,63 +362,28 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Los métodos marcados con [TestCleanup] deberían seguir el siguiente diseño para ser válidos: -- debería ser 'public' -- no debería ser 'static' -- no debería ser genérico -- no debería ser 'abstract' -- no debería tomar ningún parámetro -- el tipo de valor devuelto debería ser 'void', 'Task' o 'ValueTask' -- no debería ser 'async void' -- no debería ser un método especial (finalizador, operador...). - - - - TestCleanup method '{0}' should not take any parameter - El método TestCleanup "{0}" no debería tomar ningún parámetro - - - - TestCleanup method '{0}' should not be 'abstract' - El método TestCleanup "{0}" no debe ser "abstract" - - - - TestCleanup method '{0}' should not be 'async void' - El método TestCleanup "{0}" no debe ser "async void" - - - - TestCleanup method '{0}' should not be generic - El método TestCleanup "{0}" no debe ser genérico - - - - TestCleanup method '{0}' should not be 'static' - El método TestCleanup "{0}" no debe ser "static" +- ser "public" +- no ser "static" +- no ser genérico ni estar definido en una clase genérica +- no ser "abstract" +- no tomar ningún parámetro +- el tipo de valor devuelto debería ser "void", "Task" o "ValueTask" +- no ser "async void" +- no ser un método especial (finalizador, operador...). - - TestCleanup method '{0}' should be an 'ordinary' method - El método TestCleanup "{0}" debe ser un método "normal" - - - - TestCleanup method '{0}' should be 'public' - El método TestCleanup "{0}" debe ser "public" - - - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - El método TestCleanup "{0}" debe devolver "void", "Task" o "ValueTask" + + TestCleanup method '{0}' signature is invalid + La signatura "{0}" del método TestCleanup no es válida @@ -561,63 +436,28 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Los métodos marcados con [TestInitialize] deberían seguir el siguiente diseño para ser válidos: -- debería ser 'public' -- no debería ser 'static' -- no debería ser genérico -- no debería ser 'abstract' -- no debería tomar ningún parámetro -- el tipo de valor devuelto debería ser 'void', 'Task' o 'ValueTask' -- no debería ser 'async void' -- no debería ser un método especial (finalizador, operador...). - - - - TestInitialize method '{0}' should not take any parameter - El método TestInitialize "{0}" no debería tomar ningún parámetro - - - - TestInitialize method '{0}' should not be 'abstract' - El método TestInitialize "{0}" no debe ser "abstract" - - - - TestInitialize method '{0}' should not be 'async void' - El método TestInitialize "{0}" no debe ser "async void" - - - - TestInitialize method '{0}' should not be generic - El método TestInitialize "{0}" no debe ser genérico +- ser "public" +- no ser "static" +- no ser genérico ni estar definido en una clase genérica +- no ser "abstract" +- no tomar ningún parámetro +- el tipo de valor devuelto debería ser "void", "Task" o "ValueTask" +- no ser "async void" +- no ser un método especial (finalizador, operador...). - - TestInitialize method '{0}' should not be 'static' - El método TestInitialize "{0}" no debe ser "static" - - - - TestInitialize method '{0}' should be an 'ordinary' method - El método TestInitialize "{0}" debe ser un método "normal" - - - - TestInitialize method '{0}' should be 'public' - El método TestInitialize "{0}" debe ser "public" - - - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - El método TestInitialize "{0}" debe devolver "void", "Task" o "ValueTask" + + TestInitialize method '{0}' signature is invalid + La signatura "{0}" del método TestInitialize no es válida @@ -704,6 +544,31 @@ El método de prueba no debería ignorarse + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + Class '{0}' contains test methods and should be marked with '[TestClass]' + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + Type containing '[TestMethod]' should be marked with '[TestClass]' + + + + Asynchronous test fixture methods do not require the 'Async' suffix + Los métodos asincrónicos de accesorio de prueba no requieren el sufijo "Async". + + + + Asynchronous test methods do not require the 'Async' suffix + Los métodos de prueba asincrónicos no requieren el sufijo "Async". + + [{0}] can only be set on methods marked with [TestMethod] [{0}] solo se puede establecer en métodos marcados con [TestMethod] diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf index 95152f3832..59b6845b30 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf @@ -4,61 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Les méthodes marquées par [AssemblyCleanup] doivent respecter le schéma suivant pour être valides : -– Elle doit être « public » -– Elle doit être « static » -– Elle ne doit pas être générique -– Elle ne doit accepter aucun paramètre -– Elle doit renvoyer « void », « Task » ou « ValueTask » -– Elle ne doit pas être « async void » -– Il ne doit pas s’agir d’une méthode spéciale (finaliseur, opérateur...). - - - - AssemblyCleanup method '{0}' should not take any parameter - La méthode AssemblyCleanup « {0} » ne doit accepter aucun paramètre - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - La méthode AssemblyCleanup « {0} » ne peut pas être déclarée sur une classe générique - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - La méthode AssemblyCleanup « {0} » doit retourner « void », « Task » ou « ValueTask » +- être « public » +- être "statique" +- ne pas être générique ni défini sur une classe générique +- ne pas prendre de paramètre +- le type de retour doit être « void », « Task » ou « ValueTask » +- ne pas être "vide asynchrone" +- ne pas être une méthode particulière (finaliseur, opérateur...). - - AssemblyCleanup method '{0}' should not be generic - La méthode AssemblyCleanup « {0} » ne doit pas être générique - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - La méthode AssemblyCleanup« {0} » doit être une méthode « ordinary » - - - - AssemblyCleanup method '{0}' should be 'public' - La méthode AssemblyCleanup « {0} » doit être « public » - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - La méthode AssemblyCleanup « {0} » doit retourner « void », « Task » ou « ValueTask » - - - - AssemblyCleanup method '{0}' should be 'static' - La méthode AssemblyCleanup « {0} » doit être « static » + + AssemblyCleanup method '{0}' signature is invalid + La signature de la méthode AssemblyCleanup '{0}' n’est pas valide @@ -68,66 +33,41 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Les méthodes marquées par [AssemblyInitialize] doivent respecter le schéma suivant pour être valides : -– Elle doit être « public » -– Elle doit être « static » -– Elle ne doit pas être générique -– Elle doit prendre un paramètre de type « TestContext » -– Elle doit renvoyer « void », « Task » ou « ValueTask » -– Elle ne doit pas être « async void » -– Il ne doit pas s’agir d’une méthode spéciale (finaliseur, opérateur...). - - - - AssemblyInitialize method '{0}' can't be declared on a generic class - La méthode AssemblyInitialize « {0} » ne peut pas être déclarée sur une classe générique - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - La méthode AssemblyInitialize « {0} » doit retourner « void », « Task » ou « Value Task » - - - - AssemblyInitialize method '{0}' should not be generic - La méthode AssemblyInitialize « {0} » ne doit pas être générique - - - - AssemblyInitialize method '{0}' should be an 'ordinary' method - La méthode AssemblyInitialize « {0} » doit être une méthode « ordinary » - - - - AssemblyInitialize method '{0}' should be 'public' - La méthode AssemblyInitialize « {0} » doit être « public » +- être « public » +- être "statique" +- ne pas être générique ni être défini sur une classe générique +- prendre un paramètre unique de type 'TestContext' +- le type de retour doit être « void », « Task » ou « ValueTask » +- ne pas être "vide asynchrone" +- ne pas être une méthode particulière (finaliseur, opérateur...). - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - La méthode AssemblyInitialize « {0} » doit retourner « void », « Task » ou « Value Task » + + AssemblyInitialize method '{0}' signature is invalid + La signature de la méthode AssemblyInitialize '{0}' n’est pas valide - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - La méthode AssemblyInitialize « {0} » doit prendre un seul paramètre de type « TestContext » + + AssemblyInitialize methods should have valid layout + La méthode AssemblyInitialize doit avoir une disposition valide - - AssemblyInitialize method '{0}' should be 'static' - La méthode AssemblyInitialize « {0} » doit être « static » + + Prefer adding an additional assertion that checks for null + Préférer l’ajout d’une assertion supplémentaire qui recherche la valeur null - - AssemblyInitialize methods should have valid layout - La méthode AssemblyInitialize doit avoir une disposition valide + + Avoid conditional access in assertions + Éviter l’accès conditionnel dans les assertions @@ -162,61 +102,26 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Les méthodes marquées par [ClassCleanup] doivent respecter le schéma suivant pour être valides : -– Elle doit être « public » -– Elle ne doit pas être « static » -– Elle ne doit pas être générique -– Elle ne doit accepter aucun paramètre -– Elle doit renvoyer « void », « Task » ou « ValueTask » -– Elle ne doit pas être « async void » -– Il ne doit pas s’agir d’une méthode spéciale (finaliseur, opérateur...). - - - - ClassCleanup method '{0}' should not take any parameter - La méthode ClassCleanup « {0} » ne doit accepter aucun paramètre - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - La méthode ClassCleanup « {0} » ne peut pas être déclarée sur une classe générique sans que le mode `InheritanceBehavior` soit défini - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - La méthode ClassCleanup « {0} » doit retourner « void », « Task » ou « ValueTask » +- être « public » +- ne pas être 'statique' +- ne pas être générique ni défini sur une classe générique +- ne pas prendre de paramètre +- le type de retour doit être « void », « Task » ou « ValueTask » +- ne pas être "vide asynchrone" +- ne pas être une méthode particulière (finaliseur, opérateur...). - - ClassCleanup method '{0}' should not be generic - La méthode ClassCleanup « {0} » ne doit pas être générique - - - - ClassCleanup method '{0}' should be an 'ordinary' method - La méthode ClassCleanup « {0} » doit être une méthode « ordinary » - - - - ClassCleanup method '{0}' should be 'public' - La méthode ClassCleanup « {0} » doit être « public » - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - La méthode ClassCleanup « {0} » doit retourner « void », « Task » ou « ValueTask » - - - - ClassCleanup method '{0}' should be 'static' - La méthode ClassCleanup « {0} » doit être « static » + + ClassCleanup method '{0}' signature is invalid + La signature de la méthode ClassCleanup '{0}' n’est pas valide @@ -226,61 +131,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Les méthodes marquées par [ClassInitialize] doivent respecter le schéma suivant pour être valides : -– Elle doit être « public » -– Elle doit être « static » -– Elle ne doit pas être générique -– Elle doit prendre un paramètre de type « TestContext » -– Elle doit renvoyer « void », « Task » ou « ValueTask » -– Elle ne doit pas être « async void » -– Il ne doit pas s’agir d’une méthode spéciale (finaliseur, opérateur...). - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - La méthode ClassInitialize « {0} » ne peut pas être déclarée sur une classe générique sans que le mode `InheritanceBehavior` soit défini - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - La méthode ClassInitialize « {0} » doit retourner « void », « Task » ou « ValueTask » - - - - ClassInitialize method '{0}' should not be generic - La méthode ClassInitialize « {0} » ne doit pas être générique - - - - ClassInitialize method '{0}' should be an 'ordinary' method - La méthode ClassInitialize « {0} » doit être une méthode « ordinary » - - - - ClassInitialize method '{0}' should be 'public' - La méthode ClassInitialize « {0} » doit être « public » - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - La méthode ClassInitialize « {0} » doit retourner « void », « Task » ou « ValueTask » +- être « public » +- être "statique" +- ne pas être générique ni être défini sur une classe générique +- prendre un paramètre unique de type 'TestContext' +- le type de retour doit être « void », « Task » ou « ValueTask » +- ne pas être "vide asynchrone" +- ne pas être une méthode particulière (finaliseur, opérateur...). - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - La méthode ClassInitialize « {0} » doit prendre un seul paramètre de type « TestContext » - - - - ClassInitialize method '{0}' should be 'static' - La méthode ClassInitialize « {0} » doit être « static » + + ClassInitialize method '{0}' signature is invalid + La signature de la méthode ClassInitialize '{0}' n’est pas valide @@ -339,6 +209,21 @@ Ne pas stocker TestContext dans un membre statique + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + « System.ComponentModel.DescriptionAttribute » n’a aucun effet dans le contexte des tests et vous vouliez probablement utiliser « Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute » à la place. + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + Vous vouliez utiliser « Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute » ? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + « System.ComponentModel.DescriptionAttribute » n’a aucun effet sur les méthodes de test + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert Utilisez « Assert.Fail » à la place d’une assertion « Assert.{0} » toujours en échec @@ -349,6 +234,16 @@ Utilisez « Assert.Fail » à la place d’une assertion toujours en échec + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods Préférer les constructeurs aux méthodes TestInitialize @@ -389,6 +284,21 @@ Préférer les méthodes TestInitialize aux constructeurs + + Public methods should be test methods (marked with `[TestMethod]`). + Public methods should be test methods (marked with `[TestMethod]`). + + + + Public method '{0}' should be a test method + Public method '{0}' should be a test method + + + + Public methods should be test methods + Public methods should be test methods + + It's considered a good practice to have only test classes marked public in a test project. C’est considéré comme une bonne pratique d’avoir uniquement des classes de test marquées comme publiques dans un projet de test. @@ -452,63 +362,28 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Les méthodes marquées par [TestCleanup] doivent respecter le schéma suivant pour être valides : -– Il doit être « public » -– il ne devrait pas être « static » -– il ne doit pas être générique -– il ne doit pas être « abstract » -– il ne doit accepter aucun paramètre -- doit être « void » ou renvoyer « Task » ou « ValueTask » -– il ne doit pas être « async void » -- il ne doit pas s’agir d’une méthode spéciale (finaliseur, opérateur...). - - - - TestCleanup method '{0}' should not take any parameter - La méthode TestCleanup « {0} » ne doit accepter aucun paramètre - - - - TestCleanup method '{0}' should not be 'abstract' - La méthode TestCleanup « {0} » ne doit pas être « abstract » - - - - TestCleanup method '{0}' should not be 'async void' - La méthode TestCleanup « {0} » ne doit pas être « asynch void » - - - - TestCleanup method '{0}' should not be generic - La méthode TestCleanup « {0} » ne doit pas être générique - - - - TestCleanup method '{0}' should not be 'static' - La méthode TestCleanup « {0} » ne doit pas être « static » - - - - TestCleanup method '{0}' should be an 'ordinary' method - La méthode TestCleanup « {0} » doit être une méthode « ordinary » +- être « public » +- ne pas être 'statique' +- ne pas être générique ou être défini sur une classe générique +- ne pas être 'abstract' +- ne pas prendre de paramètre +- le type de retour doit être « void », « Task » ou « ValueTask » +- ne pas être "vide asynchrone" +- ne pas être une méthode particulière (finaliseur, opérateur...). - - TestCleanup method '{0}' should be 'public' - La méthode TestCleanup « {0} » doit être « public » - - - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - La méthode TestCleanup « {0} » doit retourner « void », « Task » ou « Value Task » + + TestCleanup method '{0}' signature is invalid + La signature «{0}» de la méthode TestCleanup n’est pas valide @@ -561,63 +436,28 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Les méthodes marquées par [TestInitialize] doivent respecter le schéma suivant pour être valides : -– Il doit être « public » -– il ne devrait pas être « static » -– il ne doit pas être générique -– il ne doit pas être « abstract » -– il ne doit accepter aucun paramètre -- doit être « void » ou renvoyer « Task » ou « ValueTask » -– il ne doit pas être « async void » -- il ne doit pas s’agir d’une méthode spéciale (finaliseur, opérateur...). - - - - TestInitialize method '{0}' should not take any parameter - La méthode TestInitialize « {0} » ne doit accepter aucun paramètre - - - - TestInitialize method '{0}' should not be 'abstract' - La méthode TestInitialize « {0} » ne doit pas être « abstract » - - - - TestInitialize method '{0}' should not be 'async void' - La méthode TestInitialize « {0} » ne doit pas être « asynch void » +- être « public » +- ne pas être 'statique' +- ne pas être générique ni défini sur une classe générique +- ne pas être 'abstract' +- ne pas prendre de paramètre +- le type de retour doit être « void », « Task » ou « ValueTask » +- ne pas être "vide asynchrone" +- ne pas être une méthode particulière (finaliseur, opérateur...). - - TestInitialize method '{0}' should not be generic - La méthode TestInitialize « {0} » ne doit pas être générique - - - - TestInitialize method '{0}' should not be 'static' - La méthode TestInitialize « {0} » ne doit pas être « static » - - - - TestInitialize method '{0}' should be an 'ordinary' method - La méthode TestInitialize « {0} » doit être une méthode « ordinary » - - - - TestInitialize method '{0}' should be 'public' - La méthode TestInitialize « {0} » doit être « public » - - - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - La méthode TestInitialize « {0} » doit retourner « void », « Task » ou « Value Task » + + TestInitialize method '{0}' signature is invalid + La signature de la méthode TestInitialize '{0}' n’est pas valide @@ -704,6 +544,31 @@ La méthode de test doit être ignorée + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Le type contenant « [TestMethod] » doit être marqué avec « [TestClass] », sans quoi la méthode de test sera ignorée silencieusement. + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + La classe « {0} » contient des méthodes de test et doit être marquée avec « [TestClass] » + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + Le type contenant « [TestMethod] » doit être marqué avec « [TestClass] » + + + + Asynchronous test fixture methods do not require the 'Async' suffix + Les méthodes de fixture de test asynchrones ne nécessitent pas le suffixe « Async » + + + + Asynchronous test methods do not require the 'Async' suffix + Les méthodes de test asynchrones ne nécessitent pas le suffixe 'Async' + + [{0}] can only be set on methods marked with [TestMethod] [{0}] ne peut être défini que sur les méthodes marquées avec [TestMethod] diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf index 654cb4ca02..1bda4e4e12 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf @@ -4,61 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). I metodi contrassegnati con [AssemblyCleanup] devono seguire il layout seguente per essere validi: -- Deve essere 'public' -- Deve essere 'static' -- Non deve essere generico -- Non deve accettare alcun parametro -- Il tipo restituito deve essere "void", "Task" o "ValueTask" -- Non deve essere 'async void' -- Non deve essere un metodo speciale (finalizzatore, operatore...). - - - - AssemblyCleanup method '{0}' should not take any parameter - Il metodo AssemblyCleanup '{0}' non deve accettare alcun parametro - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - Il metodo AssemblyCleanup '{0}' non può essere dichiarato in una classe generica - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Il metodo AssemblyCleanup '{0}' deve restituire 'void', 'Task' o 'ValueTask' - - - - AssemblyCleanup method '{0}' should not be generic - Il metodo AssemblyCleanup '{0}' non deve essere generico - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - Il metodo AssemblyCleanup '{0}' deve essere un metodo 'ordinary' - - - - AssemblyCleanup method '{0}' should be 'public' - Il metodo TestCleanup '{0}' deve essere 'public' - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Il metodo AssemblyCleanup '{0}' deve restituire 'void', 'Task' o 'ValueTask' +- devono essere 'public' +- devono essere 'static' +- non devono essere generici né definiti in una classe generica +- non devono accettare alcun parametro +- il tipo restituito deve essere 'void', 'Task' o 'ValueTask' +- non devono essere 'async void' +- non devono essere un metodo speciale (finalizzatore, operatore...). - - AssemblyCleanup method '{0}' should be 'static' - Il metodo AssemblyCleanup '{0}' deve essere 'static' + + AssemblyCleanup method '{0}' signature is invalid + La firma del metodo AssemblyCleanup '{0}' non è valida @@ -68,66 +33,41 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). I metodi contrassegnati con [AssemblyInitialize] devono seguire il layout seguente per essere validi: -- Deve essere 'public' -- Deve essere 'static' -- Non deve essere generico -- deve accettare un parametro di tipo 'TestContext' -- Il tipo restituito deve essere "void", "Task" o "ValueTask" -- Non deve essere 'async void' -- Non deve essere un metodo speciale (finalizzatore, operatore...). - - - - AssemblyInitialize method '{0}' can't be declared on a generic class - Il metodo AssemblyInitialize '{0}' non può essere dichiarato in una classe generica - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Il metodo AssemblyInitialize '{0}' deve restituire 'void', 'Task' o 'ValueTask' - - - - AssemblyInitialize method '{0}' should not be generic - Il metodo AssemblyInitialize '{0}' non deve essere generico - - - - AssemblyInitialize method '{0}' should be an 'ordinary' method - Il metodo AssemblyInitialize '{0}' deve essere un metodo 'ordinary' - - - - AssemblyInitialize method '{0}' should be 'public' - Il metodo AssemblyInitialize '{0}' deve essere 'public' +- devono essere 'public' +- devono essere 'static' +- non devono essere generici né essere definiti in una classe generica +- devono accettare un singolo parametro di tipo 'TestContext' +- il tipo restituito deve essere 'void', 'Task' o 'ValueTask' +- non devono essere 'async void' +- non devono essere un metodo speciale (finalizzatore, operatore...). - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Il metodo AssemblyInitialize '{0}' deve restituire 'void', 'Task' o 'ValueTask' + + AssemblyInitialize method '{0}' signature is invalid + La firma del metodo AssemblyInitialize '{0}' non è valida - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - Il metodo AssemblyInitialize '{0}' deve accettare un singolo parametro di tipo 'TestContext' + + AssemblyInitialize methods should have valid layout + Il metodo AssemblyInitialize deve avere un layout valido - - AssemblyInitialize method '{0}' should be 'static' - Il metodo AssemblyInitialize '{0}' deve essere 'static' + + Prefer adding an additional assertion that checks for null + Preferire l'aggiunta di un'ulteriore asserzione che controlli la presenza di valori null - - AssemblyInitialize methods should have valid layout - Il metodo AssemblyInitialize deve avere un layout valido + + Avoid conditional access in assertions + Evitare l'accesso condizionale nelle asserzioni @@ -162,61 +102,26 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). I metodi contrassegnati con [ClassCleanup] devono seguire il layout seguente per essere validi: -- Deve essere 'public' -- Non deve essere 'static' -- Non deve essere generico -- Non deve accettare alcun parametro -- Il tipo restituito deve essere "void", "Task" o "ValueTask" -- Non deve essere 'async void' -- Non deve essere un metodo speciale (finalizzatore, operatore...). - - - - ClassCleanup method '{0}' should not take any parameter - Il metodo ClassCleanup '{0}' non deve accettare alcun parametro - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - Il metodo ClassCleanup '{0}' non può essere dichiarato in una classe generica senza la modalità 'InheritanceBehavior' impostata - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Il metodo ClassCleanup '{0}' deve restituire 'void', 'Task' o 'ValueTask' - - - - ClassCleanup method '{0}' should not be generic - Il metodo ClassCleanup '{0}' non deve essere generico - - - - ClassCleanup method '{0}' should be an 'ordinary' method - Il metodo ClassCleanup '{0}' deve essere un metodo 'ordinary' - - - - ClassCleanup method '{0}' should be 'public' - Il metodo ClassCleanup '{0}' deve essere 'public' - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Il metodo ClassCleanup '{0}' deve restituire 'void', 'Task' o 'ValueTask' +- devono essere 'public' +- non devono essere 'static' +- non devono essere generici né definiti in una classe generica +- non devono accettare alcun parametro +- il tipo restituito deve essere 'void', 'Task' o 'ValueTask' +- non devono essere 'async void' +- non devono essere un metodo speciale (finalizzatore, operatore...). - - ClassCleanup method '{0}' should be 'static' - Il metodo ClassCleanup '{0}' deve essere 'static' + + ClassCleanup method '{0}' signature is invalid + La firma del metodo ClassCleanup '{0}' non è valida @@ -226,61 +131,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). I metodi contrassegnati con [ClassInitialize] devono seguire il layout seguente per essere validi: -- Deve essere 'public' -- Deve essere 'static' -- Non deve essere generico -- deve accettare un parametro di tipo 'TestContext' -- Il tipo restituito deve essere "void", "Task" o "ValueTask" -- Non deve essere 'async void' -- Non deve essere un metodo speciale (finalizzatore, operatore...). - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - Il metodo ClassInitialize '{0}' non può essere dichiarato in una classe generica senza la modalità 'InheritanceBehavior' impostata - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Il metodo ClassInitialize '{0}' deve restituire 'void', 'Task' o 'ValueTask' - - - - ClassInitialize method '{0}' should not be generic - Il metodo ClassInitialize '{0}' non deve essere generico - - - - ClassInitialize method '{0}' should be an 'ordinary' method - Il metodo ClassInitialize '{0}' deve essere un metodo 'ordinary' - - - - ClassInitialize method '{0}' should be 'public' - Il metodo ClassInitialize '{0}' deve essere 'public' - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Il metodo ClassInitialize '{0}' deve restituire 'void', 'Task' o 'ValueTask' - - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - Il metodo ClassInitialize '{0}' deve accettare un singolo parametro di tipo 'TestContext' +- devono essere 'public' +- devono essere 'static' +- non devono essere generici né essere definiti in una classe generica +- devono accettare un singolo parametro di tipo 'TestContext' +- il tipo restituito deve essere 'void', 'Task' o 'ValueTask' +- non devono essere 'async void' +- non devono essere un metodo speciale (finalizzatore, operatore...). - - ClassInitialize method '{0}' should be 'static' - Il metodo ClassInitialize '{0}' deve essere 'static' + + ClassInitialize method '{0}' signature is invalid + La firma del metodo ClassInitialize '{0}' non è valida @@ -339,6 +209,21 @@ Non archiviare TestContext in un membro statico + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + 'System.ComponentModel.DescriptionAttribute' non ha alcun effetto in un contesto di test ed è probabile che si intendesse usare 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'. + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + Si intendeva usare 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + 'System.ComponentModel.DescriptionAttribute' non ha alcun effetto sui metodi di test. + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert Usare 'Assert.Fail' invece di un'asserzione 'Assert.{0}' che ha sempre esito negativo. @@ -349,6 +234,16 @@ Usare 'Assert.Fail' invece di un'asserzione che ha sempre esito negativo + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods Preferisci costruttori rispetto ai metodi TestInitialize @@ -389,6 +284,21 @@ Preferisci i metodi TestInitialize rispetto ai costruttori + + Public methods should be test methods (marked with `[TestMethod]`). + Public methods should be test methods (marked with `[TestMethod]`). + + + + Public method '{0}' should be a test method + Public method '{0}' should be a test method + + + + Public methods should be test methods + Public methods should be test methods + + It's considered a good practice to have only test classes marked public in a test project. È consigliabile che solo le classi di test siano contrassegnate come pubbliche in un progetto di test. @@ -452,63 +362,28 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). I metodi contrassegnati con [TestCleanup] devono seguire il layout seguente per essere validi: -- Deve essere 'public' -- Non deve essere 'static' -- Non deve essere generico -- Non deve essere 'abstract' -- Non deve accettare alcun parametro -- Il tipo restituito deve essere "void", "Task" o "ValueTask" -- Non deve essere 'async void' -- Non deve essere un metodo speciale (finalizzatore, operatore...). - - - - TestCleanup method '{0}' should not take any parameter - Il metodo TestCleanup '{0}' non deve accettare alcun parametro - - - - TestCleanup method '{0}' should not be 'abstract' - Il metodo TestCleanup '{0}' non deve essere 'abstract' - - - - TestCleanup method '{0}' should not be 'async void' - Il metodo TestCleanup '{0}' non deve essere 'async void' - - - - TestCleanup method '{0}' should not be generic - Il metodo TestCleanup '{0}' non deve essere generico - - - - TestCleanup method '{0}' should not be 'static' - Il metodo TestCleanup '{0}' non deve essere 'static' - - - - TestCleanup method '{0}' should be an 'ordinary' method - Il metodo TestCleanup '{0}' deve essere un metodo 'ordinary' - - - - TestCleanup method '{0}' should be 'public' - Il metodo TestCleanup '{0}' deve essere 'public' +- devono essere 'public' +- non devono essere 'static' +- non devono essere generici o definiti in una classe generica +- non devono essere 'abstract' +- non devono accettare alcun parametro +- il tipo restituito deve essere 'void', 'Task' o 'ValueTask' +- non devono essere 'async void' +- non devono essere un metodo speciale (finalizzatore, operatore...). - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Il metodo TestCleanup '{0}' deve restituire 'void', 'Task' o 'ValueTask' + + TestCleanup method '{0}' signature is invalid + La firma del metodo TestCleanup '{0}' non è valida @@ -561,63 +436,28 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). I metodi contrassegnati con [TestInitialize] devono seguire il layout seguente per essere validi: -- Deve essere 'public' -- Non deve essere 'static' -- Non deve essere generico -- Non deve essere 'abstract' -- Non deve accettare alcun parametro -- Il tipo restituito deve essere "void", "Task" o "ValueTask" -- Non deve essere 'async void' -- Non deve essere un metodo speciale (finalizzatore, operatore...). - - - - TestInitialize method '{0}' should not take any parameter - Il metodo TestInitialize '{0}' non deve accettare alcun parametro - - - - TestInitialize method '{0}' should not be 'abstract' - Il metodo TestInitialize '{0}' non deve essere 'abstract' - - - - TestInitialize method '{0}' should not be 'async void' - Il metodo TestInitialize '{0}' non deve essere 'async void' - - - - TestInitialize method '{0}' should not be generic - Il metodo TestInitialize '{0}' non deve essere generico - - - - TestInitialize method '{0}' should not be 'static' - Il metodo TestInitialize '{0}' non deve essere 'static' - - - - TestInitialize method '{0}' should be an 'ordinary' method - Il metodo TestInitialize '{0}' deve essere un metodo 'ordinary' - - - - TestInitialize method '{0}' should be 'public' - Il metodo TestInitialize '{0}' deve essere 'public' +- devono essere 'public' +- non devono essere 'static' +- non devono essere generici né definiti in una classe generica +- non devono essere 'abstract' +- non devono accettare alcun parametro +- il tipo restituito deve essere 'void', 'Task' o 'ValueTask' +- non devono essere 'async void' +- non devono essere un metodo speciale (finalizzatore, operatore...). - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Il metodo TestInitialize '{0}' deve restituire 'void', 'Task' o 'ValueTask' + + TestInitialize method '{0}' signature is invalid + La firma del metodo TestInitialize '{0}' non è valida @@ -704,6 +544,31 @@ Il metodo di test non deve essere ignorato + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Il tipo contenente '[TestMethod]' deve essere contrassegnato con '[TestClass]', altrimenti il metodo di test verrà ignorato automaticamente. + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + La classe '{0}' contiene metodi di test e deve essere contrassegnata come '[TestClass]' + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + Il tipo contenente '[TestMethod]' deve essere contrassegnato con '[TestClass]' + + + + Asynchronous test fixture methods do not require the 'Async' suffix + I metodi di fixture di test asincroni non richiedono il suffisso 'Async' + + + + Asynchronous test methods do not require the 'Async' suffix + I metodi di test asincroni non richiedono il suffisso 'Async' + + [{0}] can only be set on methods marked with [TestMethod] [{0}] può essere impostato solo su metodi contrassegnati con [TestMethod] diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf index cfafb72773..6462b9ee61 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf @@ -4,61 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [AssemblyCleanup] でマークされたメソッドを有効にするには、次のレイアウトに従う必要があります: -- 'public' である必要があります -- 'static' である必要があります -- ジェネリックにすることはできません -- パラメーターを受け取ることができません +- 'public' である +- 'static' である +- ジェネリックではなく、ジェネリック クラスでも定義されていない +- パラメーターを受け取りません - 戻り値の型が 'void'、'Task'、または 'ValueTask' である必要があります -- 'async void' にすることはできません -- 特殊なメソッド (ファイナライザー、演算子...) にすることはできません。 - - - - AssemblyCleanup method '{0}' should not take any parameter - AssemblyCleanup メソッド '{0}' はパラメーターを受け取ることができません - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - AssemblyCleanup メソッド '{0}' をジェネリック クラスで宣言することはできません - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyCleanup メソッド '{0}' は 'void'、'Task'、または 'ValueTask' を返す必要があります - - - - AssemblyCleanup method '{0}' should not be generic - AssemblyCleanup メソッド '{0}' をジェネリックにすることはできません - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - AssemblyCleanup メソッド '{0}' は 'ordinary' メソッドである必要があります - - - - AssemblyCleanup method '{0}' should be 'public' - AssemblyCleanup メソッド '{0}' は 'public' である必要があります - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyCleanup メソッド '{0}' は 'void'、'Task'、または 'ValueTask' を返す必要があります +- 'async void' ではありません +- 特殊なメソッド (ファイナライザー、演算子...) ではありません。 - - AssemblyCleanup method '{0}' should be 'static' - AssemblyCleanup メソッド '{0}' を 'static' にすることはできません + + AssemblyCleanup method '{0}' signature is invalid + AssemblyCleanup メソッド '{0}' シグネチャが無効です @@ -68,66 +33,41 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [AssemblyInitialize] でマークされたメソッドを有効にするには、次のレイアウトに従う必要があります: -- 'public' である必要があります -- 'static' である必要があります -- ジェネリックにすることはできません -- 'TestContext' 型のパラメーターを 1 つ受け取る必要があります +- 'public' である +- 'static' である +- ジェネリックではなく、ジェネリック クラスでも定義されていない +- 'TestContext' 型のパラメーターを 1 つ受け取ります - 戻り値の型が 'void'、'Task'、または 'ValueTask' である必要があります -- 'async void' にすることはできません -- 特殊なメソッド (ファイナライザー、演算子...) にすることはできません。 - - - - AssemblyInitialize method '{0}' can't be declared on a generic class - AssemblyInitialize メソッド '{0}' をジェネリック クラスで宣言することはできません - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyInitialize メソッド '{0}' は 'void'、'Task'、または 'ValueTask' を返す必要があります +- 'async void' ではありません +- 特殊なメソッド (ファイナライザー、演算子...) ではありません。 - - AssemblyInitialize method '{0}' should not be generic - AssemblyInitialize メソッド '{0}' をジェネリックにすることはできません + + AssemblyInitialize method '{0}' signature is invalid + AssemblyInitialize メソッド '{0}' シグネチャが無効です - - AssemblyInitialize method '{0}' should be an 'ordinary' method - AssemblyInitialize メソッド '{0}' は 'ordinary' メソッドである必要があります - - - - AssemblyInitialize method '{0}' should be 'public' - AssemblyInitialize メソッド '{0}' は 'public' である必要があります - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyInitialize メソッド '{0}' は 'void'、'Task'、または 'ValueTask' を返す必要があります + + AssemblyInitialize methods should have valid layout + AssemblyInitialize メソッドには有効なレイアウトが必要です - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - AssemblyInitialize メソッド '{0}' は、'TestContext' 型の単一パラメーターを受け取る必要があります + + Prefer adding an additional assertion that checks for null + 追加のアサーションによる null チェックを推奨する - - AssemblyInitialize method '{0}' should be 'static' - AssemblyInitialize メソッド '{0}' は 'static' にする必要があります - - - - AssemblyInitialize methods should have valid layout - AssemblyInitialize メソッドには有効なレイアウトが必要です + + Avoid conditional access in assertions + アサーションの中で条件付きアクセスの使用を避ける @@ -162,61 +102,26 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [ClassCleanup] でマークされたメソッドを有効にするには、次のレイアウトに従う必要があります: -- 'public' である必要があります -- 'static' にすることはできません -- ジェネリックにすることはできません -- パラメーターを受け取ることができません +- 'public' である +- 'static' ではありません +- ジェネリックではなく、ジェネリック クラスでも定義されていない +- パラメーターを受け取りません - 戻り値の型が 'void'、'Task'、または 'ValueTask' である必要があります -- 'async void' にすることはできません -- 特殊なメソッド (ファイナライザー、演算子...) にすることはできません。 - - - - ClassCleanup method '{0}' should not take any parameter - ClassCleanup メソッド '{0}' はパラメーターを受け取ることができません - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - ClassCleanup メソッド '{0}' は、`InheritanceBehavior` モードが設定されていないと、ジェネリック クラスで宣言できません - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassCleanup メソッド '{0}' は 'void'、'Task'、または 'ValueTask' を返す必要があります +- 'async void' ではありません +- 特殊なメソッド (ファイナライザー、演算子...) ではありません。 - - ClassCleanup method '{0}' should not be generic - ClassCleanup メソッド '{0}' をジェネリックにすることはできません - - - - ClassCleanup method '{0}' should be an 'ordinary' method - ClassCleanup メソッド '{0}' は 'ordinary' メソッドである必要があります - - - - ClassCleanup method '{0}' should be 'public' - ClassCleanup メソッド '{0}' は 'public' である必要があります - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassCleanup メソッド '{0}' は 'void'、'Task'、または 'ValueTask' を返す必要があります - - - - ClassCleanup method '{0}' should be 'static' - ClassCleanup メソッド '{0}' は 'static' である必要があります + + ClassCleanup method '{0}' signature is invalid + ClassCleanup メソッド '{0}' シグネチャが無効です @@ -226,61 +131,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [ClassInitialize] でマークされたメソッドを有効にするには、次のレイアウトに従う必要があります: -- 'public' である必要があります -- 'static' である必要があります -- ジェネリックにすることはできません -- 'TestContext' 型のパラメーターを 1 つ受け取る必要があります +- 'public' である +- 'static' である +- ジェネリックではなく、ジェネリック クラスでも定義されていない +- 'TestContext' 型のパラメーターを 1 つ受け取ります - 戻り値の型が 'void'、'Task'、または 'ValueTask' である必要があります -- 'async void' にすることはできません -- 特殊なメソッド (ファイナライザー、演算子...) にすることはできません。 - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - ClassInitialize メソッド '{0}' は、`InheritanceBehavior` モードが設定されていないと、 ジェネリック クラスで宣言できません - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassInitialize メソッド '{0}' は 'void'、'Task'、または 'ValueTask' を返す必要があります - - - - ClassInitialize method '{0}' should not be generic - ClassInitialize メソッド '{0}' をジェネリックにすることはできません - - - - ClassInitialize method '{0}' should be an 'ordinary' method - ClassInitialize メソッド '{0}' は 'ordinary' メソッドである必要があります - - - - ClassInitialize method '{0}' should be 'public' - ClassInitialize メソッド '{0}' は 'public' である必要があります +- 'async void' ではありません +- 特殊なメソッド (ファイナライザー、演算子...) ではありません。 - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassInitialize メソッド '{0}' は 'void'、'Task'、または 'ValueTask' を返す必要があります - - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - ClassInitialize メソッド '{0}' は、'TestContext' 型の単一パラメーターを受け取る必要があります - - - - ClassInitialize method '{0}' should be 'static' - AssemblyInitialize メソッド '{0}' は 'static' である必要があります + + ClassInitialize method '{0}' signature is invalid + ClassInitialize メソッド '{0}' シグネチャが無効です @@ -339,6 +209,21 @@ 静的メンバーに TestContext を格納しない + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + 'System.ComponentModel.DescriptionAttribute' はテストのコンテキストでは効果がないため、代わりに 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' を使用する必要がある可能性があります。 + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' を使用する予定でしたか? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + 'System.ComponentModel.DescriptionAttribute' はテスト メソッドに影響しません + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert 常に失敗している 'Assert.{0}' アサートの代わりに 'Assert.Fail' を使用する。 @@ -349,6 +234,16 @@ 常に失敗しているアサートの代わりに 'Assert.Fail' を使用する + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods TestInitialize メソッドよりもコンストラクターを優先する @@ -389,6 +284,21 @@ コンストラクターよりも TestInitialize メソッドを優先する + + Public methods should be test methods (marked with `[TestMethod]`). + Public methods should be test methods (marked with `[TestMethod]`). + + + + Public method '{0}' should be a test method + Public method '{0}' should be a test method + + + + Public methods should be test methods + Public methods should be test methods + + It's considered a good practice to have only test classes marked public in a test project. テスト プロジェクトでは、テスト クラスのみをパブリックとしてマークすることをお勧めします。 @@ -452,63 +362,28 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [TestCleanup] でマークされたメソッドを有効にするには、次のレイアウトに従う必要があります: -- 'public' である必要があります -- 'static' にすることはできません -- ジェネリックにすることはできません -- 'abstract' にすることはできません -- パラメーターを受け取ることができません +- 'public' である +- 'static' ではありません +- ジェネリックではないか、ジェネリック クラスで定義されている +- 'abstract' ではありません +- パラメーターを受け取りません - 戻り値の型が 'void'、'Task'、または 'ValueTask' である必要があります -- 'async void' にすることはできません -- 特殊なメソッド (ファイナライザー、演算子...) にすることはできません。 - - - - TestCleanup method '{0}' should not take any parameter - TestCleanup メソッド '{0}' はパラメーターを受け取ることができません - - - - TestCleanup method '{0}' should not be 'abstract' - TestCleanup メソッド '{0}' を 'abstract' にすることはできません - - - - TestCleanup method '{0}' should not be 'async void' - TestCleanup メソッド '{0}' を 'async void' にすることはできません - - - - TestCleanup method '{0}' should not be generic - TestCleanup メソッド '{0}' をジェネリックにすることはできません - - - - TestCleanup method '{0}' should not be 'static' - TestCleanup メソッド '{0}' を 'static' にすることはできません - - - - TestCleanup method '{0}' should be an 'ordinary' method - TestCleanup メソッド '{0}' は '通常' メソッドである必要があります - - - - TestCleanup method '{0}' should be 'public' - TestCleanup メソッド '{0}' は 'public' である必要があります +- 'async void' ではありません +- 特殊なメソッド (ファイナライザー、演算子...) ではありません。 - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - TestCleanup メソッド '{0}' は 'void'、'Task'、または 'ValueTask' を返す必要があります + + TestCleanup method '{0}' signature is invalid + TestCleanup メソッド '{0}' シグネチャが無効です @@ -562,63 +437,28 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [TestInitialize] でマークされたメソッドを有効にするには、次のレイアウトに従う必要があります: -- 'public' である必要があります -- 'static' にすることはできません -- ジェネリックにすることはできません -- 'abstract' にすることはできません -- パラメーターを受け取ることができません +- 'public' である +- 'static' ではありません +- ジェネリックではなく、ジェネリック クラスでも定義されていない +- 'abstract' ではありません +- パラメーターを受け取りません - 戻り値の型が 'void'、'Task'、または 'ValueTask' である必要があります -- 'async void' にすることはできません -- 特殊なメソッド (ファイナライザー、演算子...) にすることはできません。 +- 'async void' ではありません +- 特殊なメソッド (ファイナライザー、演算子...) ではありません。 - - TestInitialize method '{0}' should not take any parameter - TestInitialize メソッド '{0}' はパラメーターを受け取ることができません - - - - TestInitialize method '{0}' should not be 'abstract' - TestInitialize メソッド '{0}' を 'abstract' にすることはできません - - - - TestInitialize method '{0}' should not be 'async void' - TestInitialize メソッド '{0}' を 'async void' にすることはできません - - - - TestInitialize method '{0}' should not be generic - TestInitialize メソッド '{0}' をジェネリックにすることはできません - - - - TestInitialize method '{0}' should not be 'static' - TestInitialize メソッド '{0}' を 'static' にすることはできません - - - - TestInitialize method '{0}' should be an 'ordinary' method - TestInitialize メソッド '{0}' は 'ordinary' メソッドである必要があります - - - - TestInitialize method '{0}' should be 'public' - TestInitialize メソッド '{0}' は 'public' である必要があります - - - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - TestInitialize メソッド '{0}' は 'void'、'Task'、または 'ValueTask' を返す必要があります + + TestInitialize method '{0}' signature is invalid + TestInitialize メソッド '{0}' シグネチャが無効です @@ -705,6 +545,31 @@ テスト メソッドを無視することはできません + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + 連続する型 '[TestMethod]' は '[TestClass]' でマークする必要があります。それ以外の場合、テスト メソッドは暗黙的に無視されます。 + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + クラス '{0}' にはテスト メソッドが含まれており、'[TestClass]' でマークする必要があります + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + '[TestMethod]' を含む型は '[TestClass]' でマークする必要があります + + + + Asynchronous test fixture methods do not require the 'Async' suffix + 非同期テスト フィクスチャには 'Async' サフィックスは不要です + + + + Asynchronous test methods do not require the 'Async' suffix + 非同期テスト メソッドには 'Async' サフィックスは不要です + + [{0}] can only be set on methods marked with [TestMethod] [{0}] は、[TestMethod] でマークされたメソッドにのみ設定できます diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf index ea8ed6f160..2bbcb164a9 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf @@ -4,61 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [AssemblyCleanup]으로 표시된 메서드가 유효하려면 다음 레이아웃을 따라야 합니다. -- 'public'이어야 합니다. +- 'public'이어야 합니다. - 'static'이어야 합니다. -- 제네릭이 아니어야 합니다. -- 매개 변수를 사용하지 않아야 합니다. +- 제네릭이 아니거나 제네릭 클래스에 정의되지 않음 +- 매개 변수를 사용하지 않음 - 반환 형식은 'void', 'Task' 또는 'ValueTask'여야 합니다. - 'async void'가 아니어야 합니다. -- 특수 메서드(종료자, 연산자...)가 아니어야 합니다. - - - - AssemblyCleanup method '{0}' should not take any parameter - AssemblyCleanup 메서드 '{0}'은(는) 매개 변수를 사용하지 않아야 합니다. - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - '{0}' AssemblyCleanup 메서드는 제네릭 클래스에서 선언할 수 없습니다. - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyCleanup 메서드 '{0}'은(는) 'void', 'Task' 또는 'ValueTask'를 반환해야 합니다. - - - - AssemblyCleanup method '{0}' should not be generic - AssemblyCleanup 메서드 '{0}'은(는) 제네릭이 아니어야 합니다. - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - AssemblyCleanup 메서드 '{0}'은(는) 'ordinary' 메서드여야 합니다. +- 특수 메서드(종료자, 연산자...)가 아님. - - AssemblyCleanup method '{0}' should be 'public' - AssemblyCleanup 메서드 '{0}'은(는) 'public'이어야 합니다. - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyCleanup 메서드 '{0}'은(는) 'void', 'Task' 또는 'ValueTask'를 반환해야 합니다. - - - - AssemblyCleanup method '{0}' should be 'static' - AssemblyCleanup 메서드 '{0}'은(는) 'static'이어야 합니다. + + AssemblyCleanup method '{0}' signature is invalid + AssemblyCleanup 메서드 '{0}' 시그니처가 잘못되었습니다. @@ -68,66 +33,41 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [AssemblyInitialize]로 표시된 메서드가 유효하려면 다음 레이아웃을 따라야 합니다. -- 'public'이어야 합니다. +- 'public'이어야 합니다. - 'static'이어야 합니다. -- 제네릭이 아니어야 합니다. -- 'TestContext' 형식의 매개 변수를 하나 사용해야 합니다. +- 제네릭이 아니거나 제네릭 클래스에 정의되지 않음 +- 'TestContext' 형식의 단일 매개 변수 사용 - 반환 형식은 'void', 'Task' 또는 'ValueTask'여야 합니다. - 'async void'가 아니어야 합니다. -- 특수 메서드(종료자, 연산자...)가 아니어야 합니다. - - - - AssemblyInitialize method '{0}' can't be declared on a generic class - '{0}' AssemblyInitialize 메서드는 제네릭 클래스에서 선언할 수 없습니다. - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyInitialize 메서드 '{0}'은(는) 'void', 'Task' 또는 'ValueTask'를 반환해야 합니다. - - - - AssemblyInitialize method '{0}' should not be generic - AssemblyInitialize 메서드 '{0}'은(는) 제네릭이 아니어야 합니다. +- 특수 메서드(종료자, 연산자...)가 아님. - - AssemblyInitialize method '{0}' should be an 'ordinary' method - AssemblyInitialize 메서드 '{0}'은(는) 'ordinary' 메서드여야 합니다. + + AssemblyInitialize method '{0}' signature is invalid + AssemblyInitialize 메서드 '{0}' 시그니처가 잘못되었습니다. - - AssemblyInitialize method '{0}' should be 'public' - AssemblyInitialize 메서드 '{0}'은(는) 'public'이어야 합니다. - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyInitialize 메서드 '{0}'은(는) 'void', 'Task' 또는 'ValueTask'를 반환해야 합니다. - - - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - AssemblyInitialize 메서드 '{0}'은(는) 'TestContext' 형식의 단일 매개 변수를 사용해야 합니다. + + AssemblyInitialize methods should have valid layout + AssemblyInitialize 메서드에는 유효한 레이아웃이 있어야 합니다. - - AssemblyInitialize method '{0}' should be 'static' - AssemblyInitialize 메서드 '{0}'은(는) 'static'이어야 합니다. + + Prefer adding an additional assertion that checks for null + null을 확인하는 추가 어설션을 추가하는 것이 좋습니다. - - AssemblyInitialize methods should have valid layout - AssemblyInitialize 메서드에는 유효한 레이아웃이 있어야 합니다. + + Avoid conditional access in assertions + 어설션에서 조건부 액세스 방지 @@ -162,61 +102,26 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [ClassCleanup]으로 표시된 메서드가 유효하려면 다음 레이아웃을 따라야 합니다. -- 'public'이어야 합니다. +- 'public'이어야 합니다. - 'static'이 아니어야 합니다. -- 제네릭이 아니어야 합니다. -- 매개 변수를 사용하지 않아야 합니다. +- 제네릭이 아니거나 제네릭 클래스에 정의되지 않음 +- 매개 변수를 사용하지 않음 - 반환 형식은 'void', 'Task' 또는 'ValueTask'여야 합니다. - 'async void'가 아니어야 합니다. -- 특수 메서드(종료자, 연산자...)가 아니어야 합니다. - - - - ClassCleanup method '{0}' should not take any parameter - ClassCleanup 메서드 '{0}'은(는) 매개 변수를 사용하지 않아야 합니다. - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - '{0}' ClassCleanup 메서드는 'InheritanceBehavior' 모드가 설정되지 않은 제네릭 클래스에서 선언할 수 없습니다. - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassCleanup 메서드 '{0}'은(는) 'void', 'Task' 또는 'ValueTask'를 반환해야 합니다. - - - - ClassCleanup method '{0}' should not be generic - ClassCleanup 메서드 '{0}'은(는) 제네릭이 아니어야 합니다. - - - - ClassCleanup method '{0}' should be an 'ordinary' method - ClassCleanup 메서드 '{0}'은(는) 'ordinary' 메서드여야 합니다. +- 특수 메서드(종료자, 연산자...)가 아님. - - ClassCleanup method '{0}' should be 'public' - ClassCleanup 메서드 '{0}'은(는) 'public'이어야 합니다. - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassCleanup 메서드 '{0}'은(는) 'void', 'Task' 또는 'ValueTask'를 반환해야 합니다. - - - - ClassCleanup method '{0}' should be 'static' - ClassCleanup 메서드 '{0}'은(는) 'static'이어야 합니다. + + ClassCleanup method '{0}' signature is invalid + ClassCleanup 메서드 '{0}' 시그니처가 잘못되었습니다. @@ -226,61 +131,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [ClassInitialize]로 표시된 메서드가 유효하려면 다음 레이아웃을 따라야 합니다. -- 'public'이어야 합니다. +- 'public'이어야 합니다. - 'static'이어야 합니다. -- 제네릭이 아니어야 합니다. -- 'TestContext' 형식의 매개 변수를 하나 사용해야 합니다. +- 제네릭이 아니거나 제네릭 클래스에 정의되지 않음 +- 'TestContext' 형식의 단일 매개 변수 사용 - 반환 형식은 'void', 'Task' 또는 'ValueTask'여야 합니다. - 'async void'가 아니어야 합니다. -- 특수 메서드(종료자, 연산자...)가 아니어야 합니다. - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - '{0}' ClassInitialize 메서드는 'InheritanceBehavior' 모드가 설정되지 않은 제네릭 클래스에서 선언할 수 없습니다. - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassInitialize 메서드 '{0}'은(는) 'void', 'Task' 또는 'ValueTask'를 반환해야 합니다. - - - - ClassInitialize method '{0}' should not be generic - ClassInitialize 메서드 '{0}'은(는) 제네릭이 아니어야 합니다. +- 특수 메서드(종료자, 연산자...)가 아님. - - ClassInitialize method '{0}' should be an 'ordinary' method - ClassInitialize 메서드 '{0}'은(는) 'ordinary' 메서드여야 합니다. - - - - ClassInitialize method '{0}' should be 'public' - ClassInitialize 메서드 '{0}'은(는) 'public'이어야 합니다. - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassInitialize 메서드 '{0}'은(는) 'void', 'Task' 또는 'ValueTask'를 반환해야 합니다. - - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - ClassInitialize 메서드 '{0}'은(는) 'TestContext' 형식의 단일 매개 변수를 사용해야 합니다. - - - - ClassInitialize method '{0}' should be 'static' - ClassInitialize 메서드 '{0}'은(는) 'static'이어야 합니다. + + ClassInitialize method '{0}' signature is invalid + ClassInitialize 메서드 '{0}' 시그니처가 잘못되었습니다. @@ -339,6 +209,21 @@ TestContext를 정적 멤버에 저장하지 마세요 + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + 'System.ComponentModel.DescriptionAttribute'는 테스트 컨텍스트에 영향을 주지 않으며 대신 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'를 사용하려고 한 것 같습니다. + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'를 사용하려고 했나요? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + 'System.ComponentModel.DescriptionAttribute'는 테스트 메서드에 영향을 주지 않습니다. + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert 항상 실패하는 'Assert.{0}' 어설션 대신 'Assert.Fail'을 사용합니다. @@ -349,6 +234,16 @@ 항상 실패하는 어설션 대신 'Assert.Fail' 사용 + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods TestInitialize 메서드보다 생성자 선호 @@ -389,6 +284,21 @@ 생성자보다 TestInitialize 메서드 선호 + + Public methods should be test methods (marked with `[TestMethod]`). + Public methods should be test methods (marked with `[TestMethod]`). + + + + Public method '{0}' should be a test method + Public method '{0}' should be a test method + + + + Public methods should be test methods + Public methods should be test methods + + It's considered a good practice to have only test classes marked public in a test project. 테스트 프로젝트에서 공용으로 표시된 테스트 클래스만 사용하는 것이 좋습니다. @@ -452,63 +362,28 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter -- return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - - - - TestCleanup method '{0}' should not take any parameter - TestCleanup 메서드 '{0}'은(는) 매개 변수를 사용하지 않아야 합니다. - - - - TestCleanup method '{0}' should not be 'abstract' - TestCleanup 메서드 '{0}'은(는) 'abstract'가 아니어야 합니다. - - - - TestCleanup method '{0}' should not be 'async void' - TestCleanup 메서드 '{0}'은(는) 'async void'가 아니어야 합니다. - - - - TestCleanup method '{0}' should not be generic - TestCleanup 메서드 '{0}'은(는) 제네릭이 아니어야 합니다. - - - - TestCleanup method '{0}' should not be 'static' - TestCleanup 메서드 '{0}'은(는) 'static'이 아니어야 합니다. - - - - TestCleanup method '{0}' should be an 'ordinary' method - TestCleanup 메서드 '{0}'은(는) 'ordinary' 메서드여야 합니다. - - - - TestCleanup method '{0}' should be 'public' - TestCleanup 메서드 '{0}'은(는) 'public'이어야 합니다. +- not be 'async void' +- not be a special method (finalizer, operator...). + [TestCleanup]으로 표시된 메서드가 유효하려면 다음 레이아웃을 따라야 합니다. +- 'public'이어야 합니다. +- 'static'이 아니어야 합니다. +- 제네릭이 아니거나 제네릭 클래스에 정의되지 않음 +- 'abstract'가 아니어야 합니다. +- 매개 변수를 사용하지 않음 +- 반환 형식은 'void', 'Task' 또는 'ValueTask'여야 합니다. +- 'async void'가 아니어야 합니다. +- 특수 메서드(종료자, 연산자...)가 아님. - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - TestCleanup 메서드 '{0}'은(는) 'void', 'Task' 또는 'ValueTask'를 반환해야 합니다. + + TestCleanup method '{0}' signature is invalid + TestCleanup 메서드 '{0}' 시그니처가 잘못되었습니다. @@ -562,63 +437,28 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter -- return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - - - - TestInitialize method '{0}' should not take any parameter - TestInitialize 메서드 '{0}'은(는) 매개 변수를 사용하지 않아야 합니다. - - - - TestInitialize method '{0}' should not be 'abstract' - TestInitialize 메서드 '{0}'은(는) 'abstract'가 아니어야 합니다. - - - - TestInitialize method '{0}' should not be 'async void' - TestInitialize 메서드 '{0}'은(는) 'async void'가 아니어야 합니다. - - - - TestInitialize method '{0}' should not be generic - TestInitialize 메서드 '{0}'은(는) 제네릭이 아니어야 합니다. - - - - TestInitialize method '{0}' should not be 'static' - TestInitialize 메서드 '{0}'은(는) 'static'이 아니어야 합니다. - - - - TestInitialize method '{0}' should be an 'ordinary' method - TestInitialize 메서드 '{0}'은(는) 'ordinary' 메서드여야 합니다. - - - - TestInitialize method '{0}' should be 'public' - TestInitialize 메서드 '{0}'은(는) 'public'이어야 합니다. +- not be 'async void' +- not be a special method (finalizer, operator...). + [TestInitialize]로 표시된 메서드가 유효하려면 다음 레이아웃을 따라야 합니다. +- 'public'이어야 합니다. +- 'static'이 아니어야 합니다. +- 제네릭이 아니거나 제네릭 클래스에 정의되지 않음 +- 'abstract'가 아니어야 합니다. +- 매개 변수를 사용하지 않음 +- 반환 형식은 'void', 'Task' 또는 'ValueTask'여야 합니다. +- 'async void'가 아니어야 합니다. +- 특수 메서드(종료자, 연산자...)가 아님. - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - TestInitialize 메서드 '{0}'은(는) 'void', 'Task' 또는 'ValueTask'를 반환해야 합니다. + + TestInitialize method '{0}' signature is invalid + TestInitialize 메서드 '{0}' 시그니처가 잘못되었습니다. @@ -705,6 +545,31 @@ 테스트 메서드는 무시하면 안 됩니다. + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + '[TestMethod]'를 포함하는 유형은 '[TestClass]'로 표시되어야 합니다. 그렇지 않으면 테스트 메서드가 자동으로 무시됩니다. + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + '{0}' 클래스에는 테스트 메서드가 포함되어 있으며 '[TestClass]'로 표시되어야 합니다. + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + '[TestMethod]'를 포함하는 유형은 '[TestClass]'로 표시되어야 합니다. + + + + Asynchronous test fixture methods do not require the 'Async' suffix + 비동기 테스트 fixture 메서드에는 'Async' 접미사가 필요하지 않습니다. + + + + Asynchronous test methods do not require the 'Async' suffix + 비동기 테스트 메서드에는 'Async' 접미사가 필요하지 않습니다. + + [{0}] can only be set on methods marked with [TestMethod] [{0}]은(는) [TestMethod] 표시된 메서드에만 설정할 수 있습니다. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf index e6e61ecf52..52f3ac53bc 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf @@ -4,61 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Metody oznaczone za pomocą [AssemblyCleanup] powinny być zgodne z następującym układem, aby były prawidłowe: -— Powinna być „publiczna” -— Powinna być „statyczna” -— Nie powinna być ogólna -— Nie powinna przyjmować żadnego parametru +— być „publiczne” +— być "statyczne" +— nie być ogólne ani zdefiniowane w klasie ogólnej +— nie przyjmować żadnego parametru — Zwracany typ powinien mieć wartość „void”, „Taks” lub „ValueTask” -— Nie powinna to być wartość „async void” -— Nie powinna to być metoda specjalna (finalizator, operator...). - - - - AssemblyCleanup method '{0}' should not take any parameter - Metoda AssemblyCleanup „{0}” nie powinna przyjmować żadnego parametru - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - Metody AssemblyCleanup „{0}” nie można zadeklarować w klasie ogólnej - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda AssemblyCleanup „{0}” powinna zwracać wartość „void”, „Task” lub „ValueTask” +— nie być „async void” +— powinna to być metoda specjalna (finalizator, operator itd.). - - AssemblyCleanup method '{0}' should not be generic - Metoda AssemblyCleanup „{0}” nie powinna być ogólna - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - Metoda AssemblyCleanup „{0}” powinna być metodą „zwykłą” - - - - AssemblyCleanup method '{0}' should be 'public' - Metoda AssemblyCleanup „{0}” powinna być „publiczna” - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda AssemblyCleanup „{0}” powinna zwracać wartość „void”, „Task” lub „ValueTask” - - - - AssemblyCleanup method '{0}' should be 'static' - Metoda AssemblyCleanup „{0}” powinna być „statyczna” + + AssemblyCleanup method '{0}' signature is invalid + Podpis metody AssemblyCleanup „{0}” jest nieprawidłowy @@ -68,66 +33,41 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Metody oznaczone za pomocą [AssemblyInitialize] powinny być zgodne z następującym układem, aby były prawidłowe: -— Powinna być „publiczna” -— Powinna być „statyczna” -— Nie powinna być ogólna -— Powinna przyjmować jeden parametr typu „TestContext” +— być „publiczne” +— być "statyczne" +- nie być ogólne ani być zdefiniowane w klasie ogólnej +— przyjmować pojedynczy parametr typu „TestContext” — Zwracany typ powinien mieć wartość „void”, „Taks” lub „ValueTask” -— Nie powinna to być wartość „async void” -— Nie powinna to być metoda specjalna (finalizator, operator...). +— nie być „async void” +— powinna to być metoda specjalna (finalizator, operator itd.). - - AssemblyInitialize method '{0}' can't be declared on a generic class - Metody AssemblyInitialize „{0}” nie można zadeklarować w klasie ogólnej + + AssemblyInitialize method '{0}' signature is invalid + Podpis metody AssemblyInitialize „{0}” jest nieprawidłowy - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda AssemblyInitialize „{0}” powinna zwracać wartość „void”, „Task” lub „ValueTask” - - - - AssemblyInitialize method '{0}' should not be generic - Metoda AssemblyInitialize „{0}” nie powinna być ogólna - - - - AssemblyInitialize method '{0}' should be an 'ordinary' method - Metoda AssemblyInitialize „{0}” powinna być metodą „zwykłą” - - - - AssemblyInitialize method '{0}' should be 'public' - Metoda AssemblyInitialize „{0}” powinna być „publiczna” - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda AssemblyInitialize „{0}” powinna zwracać wartość „void”, „Task” lub „ValueTask” - - - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - Metoda AssemblyInitialize „{0}” powinna przyjmować jeden parametr typu „TestContext” + + AssemblyInitialize methods should have valid layout + Metody AssemblyInitialize powinny mieć prawidłowy układ - - AssemblyInitialize method '{0}' should be 'static' - Metoda AssemblyInitialize „{0}” nie powinna być „statyczna” + + Prefer adding an additional assertion that checks for null + Preferuj dodawanie dodatkowej asercji, która sprawdza pod kątem wartości null - - AssemblyInitialize methods should have valid layout - Metody AssemblyInitialize powinny mieć prawidłowy układ + + Avoid conditional access in assertions + Unikaj dostępu warunkowego w asercjach @@ -162,61 +102,26 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Metody oznaczone za pomocą [ClassCleanup] powinny być zgodne z następującym układem, aby były prawidłowe: -— Powinna być „publiczna” -— Nie powinna być „statyczna” -— Nie powinna być ogólna -— Nie powinna przyjmować żadnego parametru +— być „publiczne” +— nie być „statyczne” +— nie być ogólne ani zdefiniowane w klasie ogólnej +— nie przyjmować żadnego parametru — Zwracany typ powinien mieć wartość „void”, „Taks” lub „ValueTask” -— Nie powinna to być wartość „async void” -— Nie powinna to być metoda specjalna (finalizator, operator...). - - - - ClassCleanup method '{0}' should not take any parameter - Metoda ClassCleanup „{0}” nie powinna przyjmować żadnego parametru - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - Metody ClassCleanup „{0}” nie można zadeklarować w klasie ogólnej bez ustawionego trybu „InheritanceBehavior” - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda ClassCleanup „{0}” powinna zwracać wartość „void”, „Task” lub „ValueTask” - - - - ClassCleanup method '{0}' should not be generic - Metoda ClassCleanup „{0}” nie powinna być ogólna - - - - ClassCleanup method '{0}' should be an 'ordinary' method - Metoda ClassCleanup „{0}” powinna być metodą „zwykłą” - - - - ClassCleanup method '{0}' should be 'public' - Metoda ClassCleanup „{0}” powinna być „publiczna” - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda ClassCleanup „{0}” powinna zwracać wartość „void”, „Task” lub „ValueTask” +— nie być „async void” +— powinna to być metoda specjalna (finalizator, operator itd.). - - ClassCleanup method '{0}' should be 'static' - Metoda ClassCleanup „{0}” powinna być „statyczna” + + ClassCleanup method '{0}' signature is invalid + Podpis metody ClassCleanup „{0}” jest nieprawidłowy @@ -226,61 +131,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Metody oznaczone za pomocą [ClassInitialize] powinny być zgodne z następującym układem, aby były prawidłowe: -— Powinna być „publiczna” -— Powinna być „statyczna” -— Nie powinna być ogólna -— Powinna przyjmować jeden parametr typu „TestContext” +— być „publiczne” +— być "statyczne" +- nie być ogólne ani być zdefiniowane w klasie ogólnej +— przyjmować pojedynczy parametr typu „TestContext” — Zwracany typ powinien mieć wartość „void”, „Taks” lub „ValueTask” -— Nie powinna to być wartość „async void” -— Nie powinna to być metoda specjalna (finalizator, operator...). - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - Metody ClassInitialize „{0}” nie można zadeklarować w klasie ogólnej bez ustawionego trybu „InheritanceBehavior” - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda ClassInitialize „{0}” powinna zwracać wartość „void”, „Task” lub „ValueTask” - - - - ClassInitialize method '{0}' should not be generic - Metoda ClassInitialize „{0}” nie powinna być ogólna - - - - ClassInitialize method '{0}' should be an 'ordinary' method - Metoda ClassInitialize „{0}” powinna być metodą „zwykłą” - - - - ClassInitialize method '{0}' should be 'public' - Metoda ClassInitialize „{0}” powinna być „publiczna” - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda ClassInitialize „{0}” powinna zwracać wartość „void”, „Task” lub „ValueTask” - - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - Metoda ClassInitialize „{0}” powinna przyjmować jeden parametr typu „TestContext” +— nie być „async void” +— powinna to być metoda specjalna (finalizator, operator itd.). - - ClassInitialize method '{0}' should be 'static' - Metoda ClassInitialize „{0}” powinna być „'statyczna” + + ClassInitialize method '{0}' signature is invalid + Podpis metody ClassInitialize „{0}” jest nieprawidłowy @@ -339,6 +209,21 @@ Nie przechowuj elementu TestContext w statycznym elemencie członkowskim + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + Element „System.ComponentModel.DescriptionAttribute” nie ma żadnego efektu w kontekście testów i prawdopodobnie chcesz użyć elementu „Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute”. + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + Czy chodziło Ci o użycie elementu „Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute”? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + Element „System.ComponentModel.DescriptionAttribute” nie ma wpływu na metody testowe + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert Użyj trybu „Assert.Fail” zamiast kończącej się zawsze niepowodzeniem instrukcji „Assert.{0}” @@ -349,6 +234,16 @@ Użyj trybu „Assert.Fail” zamiast kończącej się zawsze niepowodzeniem instrukcji asercji + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods Preferowanie konstruktorów niż metod TestInitialize @@ -389,6 +284,21 @@ Preferowanie metod TestInitialize niż konstruktorów + + Public methods should be test methods (marked with `[TestMethod]`). + Public methods should be test methods (marked with `[TestMethod]`). + + + + Public method '{0}' should be a test method + Public method '{0}' should be a test method + + + + Public methods should be test methods + Public methods should be test methods + + It's considered a good practice to have only test classes marked public in a test project. Uważa się, że dobrą praktyką jest oznaczanie tylko klas testowych jako publicznych w projekcie testowym. @@ -452,63 +362,28 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - Metody oznaczone znakiem [TestCleanup] powinny być zgodne z następującym układem, aby były prawidłowe: -— powinna być „publiczna” -— nie powinna być „statyczna” -— nie powinna być ogólna -— nie powinna być „abstrakcyjna” -— nie powinna przyjmować żadnego parametru -— zwracany typ powinien mieć wartość „unieważniono”, „Zadanie” lub „ValueTask” -— nie powinna to być wartość „async void” -— nie powinna to być metoda specjalna (finalizator, operator...). - - - - TestCleanup method '{0}' should not take any parameter - Metoda TestCleanup „{0}” nie powinna przyjmować żadnego parametru - - - - TestCleanup method '{0}' should not be 'abstract' - Metoda TestCleanup „{0}” nie powinna być „abstrakcyjna” - - - - TestCleanup method '{0}' should not be 'async void' - Metoda TestCleanup „{0}” nie powinna mieć wartości „async void” - - - - TestCleanup method '{0}' should not be generic - Metoda TestCleanup „{0}” nie powinna być ogólna - - - - TestCleanup method '{0}' should not be 'static' - Metoda TestCleanup „{0}” nie powinna być „statyczna” - - - - TestCleanup method '{0}' should be an 'ordinary' method - Metoda TestCleanup „{0}” powinna być metodą „zwykłą” - - - - TestCleanup method '{0}' should be 'public' - Metoda TestCleanup „{0}” powinna być „publiczna” +- not be 'async void' +- not be a special method (finalizer, operator...). + Metody oznaczone jako [TestCleanup] powinny być zgodne z następującym układem, aby były prawidłowe: +— być „publiczne” +— nie być „statyczne” +— nie być ogólne lub być zdefiniowane w klasie ogólnej +nie być „abstrakcyjne” +— nie przyjmować żadnego parametru +— zwracany typ powinien mieć wartość „void”, „Task” lub „ValueTask” +— nie być „async void” +— powinna to być metoda specjalna (finalizator, operator itd.). - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda TestCleanup „{0}” powinna zwracać wartość „unieważniono”, „Zadanie” lub „ValueTask” + + TestCleanup method '{0}' signature is invalid + Podpis metody TestCleanup „{0}” jest nieprawidłowy @@ -561,63 +436,28 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Metody oznaczone znakiem [TestInitialize] powinny być zgodne z następującym układem, aby były prawidłowe: -— powinna być „publiczna” -— nie powinna być „statyczna” -— nie powinna być ogólna -— nie powinna być „abstrakcyjna” -— nie powinna przyjmować żadnego parametru -— zwracany typ powinien mieć wartość „unieważniono”, „Zadanie” lub „ValueTask” -— nie powinna to być wartość „async void” -— nie powinna to być metoda specjalna (finalizator, operator...). - - - - TestInitialize method '{0}' should not take any parameter - Metoda TestInitialize „{0}” nie powinna przyjmować żadnego parametru - - - - TestInitialize method '{0}' should not be 'abstract' - Metoda TestInitialize „{0}” nie powinna być „abstrakcyjna” - - - - TestInitialize method '{0}' should not be 'async void' - Metoda TestInitialize „{0}” nie powinna mieć wartości "async void" - - - - TestInitialize method '{0}' should not be generic - Metoda TestInitialize „{0}” nie powinna być ogólna - - - - TestInitialize method '{0}' should not be 'static' - Metoda TestInitialize „{0}” nie powinna być „statyczna” - - - - TestInitialize method '{0}' should be an 'ordinary' method - Metoda TestInitialize „{0}” powinna być metodą „zwykłą” - - - - TestInitialize method '{0}' should be 'public' - Metoda TestInitialize „{0}” powinna być „publiczna” +— być „publiczne” +— nie być „statyczne” +— nie być ogólne ani zdefiniowane w klasie ogólnej +nie być „abstrakcyjne” +— nie przyjmować żadnego parametru +— zwracany typ powinien mieć wartość „void”, „Task” lub „ValueTask” +— nie być „async void” +— powinna to być metoda specjalna (finalizator, operator itd.). - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Metoda TestInitialize „{0}” powinna zwracać wartość „unieważniono”, „Zadanie” lub „ValueTask” + + TestInitialize method '{0}' signature is invalid + Podpis metody TestInitialize „{0}” jest nieprawidłowy @@ -704,6 +544,31 @@ Metoda testowa nie powinna być ignorowana + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Typ zwierający metodę „[TestMethod]” powinien być oznaczony klasa „[TestClass]”. W przeciwnym razie metoda testowa zostanie zignorowana w trybie dyskretnym. + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + Klasa „{0}” zawiera metody testowe i powinna być oznaczona klasą „[TestClass]” + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + Typ zawierający metodę „[TestMethod]” powinien być oznaczony klasą „[TestClass]” + + + + Asynchronous test fixture methods do not require the 'Async' suffix + Asynchroniczne metody testowe nie wymagają sufiksu „Async” + + + + Asynchronous test methods do not require the 'Async' suffix + Asynchroniczne metody testowe nie wymagają sufiksu „Async” + + [{0}] can only be set on methods marked with [TestMethod] [{0}] można ustawić tylko dla metod oznaczonych znakiem [TestMethod] diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf index e6f0e2ca20..533a692523 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf @@ -4,61 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Os métodos marcados com [AssemblyCleanup] devem seguir o seguinte layout para serem válidos: -— deve ser "público" -– ele deve ser "estático" -— não deve ser genérico -— não deve aceitar nenhum parâmetro +— ser "público" +— ser "estático" +— não ser genérico nem definido em uma classe genérica +— não aceitar nenhum parâmetro — o tipo de retorno deve ser "nulo", "Tarefa" ou "ValueTask" -— não deve ser "nulo assíncrono" -— não deve ser um método especial (finalizador, operador...). - - - - AssemblyCleanup method '{0}' should not take any parameter - O método AssemblyCleanup "{0}" não deve usar nenhum parâmetro - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - O método AssemblyCleanup '{0}' não pode ser declarado em uma classe genérica - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - O método AssemblyCleanup "{0}" deve retornar "void", "Task" ou "ValueTask" - - - - AssemblyCleanup method '{0}' should not be generic - O método AssemblyCleanup "{0}" não deve ser genérico - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - O método AssemblyCleanup "{0}" deve ser um método "comum" - - - - AssemblyCleanup method '{0}' should be 'public' - O método AssemblyCleanup "{0}" deve ser "público" - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - O método AssemblyCleanup "{0}" deve retornar "void", "Task" ou "ValueTask" +— não ser "nulo assíncrono" +— não ser um método especial (finalizador, operador...). - - AssemblyCleanup method '{0}' should be 'static' - O método AssemblyCleanup "{0}" deve ser "estático" + + AssemblyCleanup method '{0}' signature is invalid + A assinatura do método AssemblyCleanup "{0}" é inválida @@ -68,66 +33,41 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Os métodos marcados com [AssemblyInitialize] devem seguir o seguinte layout para serem válidos: -— deve ser "público" -– ele deve ser "estático" -— não deve ser genérico -– ele deve usar um parâmetro do tipo "TestContext" +— ser "público" +— ser "estático" +— não ser genérico nem ser definido em uma classe genérica +— aceitar um único parâmetro do tipo "TestContext" — o tipo de retorno deve ser "nulo", "Tarefa" ou "ValueTask" -— não deve ser "nulo assíncrono" -— não deve ser um método especial (finalizador, operador...). - - - - AssemblyInitialize method '{0}' can't be declared on a generic class - O método AssemblyInitialize '{0}' não pode ser declarado em uma classe genérica - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - O método AssemblyInitialize "{0}" deve retornar "void", "Task" ou "ValueTask" - - - - AssemblyInitialize method '{0}' should not be generic - O método AssemblyInitialize "{0}" não deve ser genérico +— não ser "nulo assíncrono" +— não ser um método especial (finalizador, operador...). - - AssemblyInitialize method '{0}' should be an 'ordinary' method - O método AssemblyInitialize "{0}" deve ser um método "comum" + + AssemblyInitialize method '{0}' signature is invalid + A assinatura do método AssemblyInitialize "{0}" é inválida - - AssemblyInitialize method '{0}' should be 'public' - O método AssemblyInitialize "{0}" deve ser "público" - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - O método AssemblyInitialize "{0}" deve retornar "void", "Task" ou "ValueTask" + + AssemblyInitialize methods should have valid layout + Os métodos AssemblyInitialize devem ter um layout válido - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - O método AssemblyInitialize "{0}" deve usar um único parâmetro do tipo "TestContext" + + Prefer adding an additional assertion that checks for null + Preferir adicionar uma declaração adicional que verifica se existe um nulo - - AssemblyInitialize method '{0}' should be 'static' - O método AssemblyInitialize "{0}" deve ser "estático" - - - - AssemblyInitialize methods should have valid layout - Os métodos AssemblyInitialize devem ter um layout válido + + Avoid conditional access in assertions + Evitar o acesso condicional nas declarações @@ -162,61 +102,26 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Os métodos marcados com [ClassCleanup] devem seguir o seguinte layout para serem válidos: -— deve ser "público" -– ele não deve ser "estático" -— não deve ser genérico -— não deve aceitar nenhum parâmetro +— ser "público" +— não ser "estático" +— não ser genérico nem definido em uma classe genérica +— não aceitar nenhum parâmetro — o tipo de retorno deve ser "nulo", "Tarefa" ou "ValueTask" -— não deve ser "nulo assíncrono" -— não deve ser um método especial (finalizador, operador...). - - - - ClassCleanup method '{0}' should not take any parameter - O método ClassCleanup "{0}" não deve usar nenhum parâmetro - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - O método ClassCleanup '{0}' não pode ser declarado em uma classe genérica sem que o modo "InheritanceBehavior" esteja definido - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - O método ClassCleanup "{0}" deve retornar "void", "Task" ou "ValueTask" - - - - ClassCleanup method '{0}' should not be generic - O método ClassCleanup "{0}" não deve ser genérico - - - - ClassCleanup method '{0}' should be an 'ordinary' method - O método ClassCleanup "{0}" deve ser um método "comum" - - - - ClassCleanup method '{0}' should be 'public' - O método ClassCleanup "{0}" deve ser "público" +— não ser "nulo assíncrono" +— não ser um método especial (finalizador, operador...). - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - O método ClassCleanup "{0}" deve retornar "void", "Task" ou "ValueTask" - - - - ClassCleanup method '{0}' should be 'static' - O método ClassCleanup "{0}" deve ser "estático" + + ClassCleanup method '{0}' signature is invalid + A assinatura do método ClassCleanup "{0}" é inválida @@ -226,61 +131,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Os métodos marcados com [ClassInitialize] devem seguir o seguinte layout para serem válidos: -— deve ser "público" -– ele deve ser "estático" -— não deve ser genérico -– ele deve usar um parâmetro do tipo "TestContext" +— ser "público" +— ser "estático" +— não ser genérico nem ser definido em uma classe genérica +— aceitar um único parâmetro do tipo "TestContext" — o tipo de retorno deve ser "nulo", "Tarefa" ou "ValueTask" -— não deve ser "nulo assíncrono" -— não deve ser um método especial (finalizador, operador...). - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - O método ClassInitialize '{0}' não pode ser declarado em uma classe genérica sem que o modo "InheritanceBehavior" esteja definido - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - O método ClassInitialize "{0}" deve retornar "void", "Task" ou "ValueTask" - - - - ClassInitialize method '{0}' should not be generic - O método ClassInitialize "{0}" não deve ser genérico - - - - ClassInitialize method '{0}' should be an 'ordinary' method - O método ClassInitialize "{0}" deve ser um método "comum" - - - - ClassInitialize method '{0}' should be 'public' - O método ClassInitialize "{0}" deve ser "público" - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - O método ClassInitialize "{0}" deve retornar "void", "Task" ou "ValueTask" - - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - O método ClassInitialize "{0}" deve usar um único parâmetro do tipo "TestContext" +— não ser "nulo assíncrono" +— não ser um método especial (finalizador, operador...). - - ClassInitialize method '{0}' should be 'static' - O método ClassInitialize "{0}" deve ser "estático" + + ClassInitialize method '{0}' signature is invalid + A assinatura do método ClassInitialize "{0}" é inválida @@ -339,6 +209,21 @@ Não armazene TestContext em um membro estático + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + 'System.ComponentModel.DescriptionAttribute' não tem efeito no contexto de testes e você provavelmente quis usar 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'. + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + Você pretendia usar 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + 'System.ComponentModel.DescriptionAttribute' não tem efeito sobre métodos de teste + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert Use "Assert.Fail" em vez de uma asserção "Assert.{0}" sempre com falha @@ -349,6 +234,16 @@ Usar "Assert.Fail" em vez de uma asserção sempre com falha + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods Preferir construtores em vez de métodos TestInitialize @@ -389,6 +284,21 @@ Preferir métodos TestInitialize em vez de construtores + + Public methods should be test methods (marked with `[TestMethod]`). + Public methods should be test methods (marked with `[TestMethod]`). + + + + Public method '{0}' should be a test method + Public method '{0}' should be a test method + + + + Public methods should be test methods + Public methods should be test methods + + It's considered a good practice to have only test classes marked public in a test project. É considerado uma boa prática ter somente classes de teste marcadas como públicas em um projeto de teste. @@ -452,63 +362,28 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - Os métodos marcados com [TestCleanup] devem seguir o seguinte layout para serem válidos: -— deve ser "público" -— não deve ser "estático" -— não deve ser genérico +- not be 'async void' +- not be a special method (finalizer, operator...). + Os métodos marcados com [TestCleanup] devem seguir o seguinte layout para serem válidos: +— ser "público" +— não ser "estático" +— não ser genérico ou ser definido em uma classe genérica — não deve ser "abstrato" -— não deve aceitar nenhum parâmetro +— não aceitar nenhum parâmetro — o tipo de retorno deve ser "nulo", "Tarefa" ou "ValueTask" -— não deve ser "nulo assíncrono" -— não deve ser um método especial (finalizador, operador...). - - - - TestCleanup method '{0}' should not take any parameter - O método de TestCleanup "{0}" não deve aceitar nenhum parâmetro - - - - TestCleanup method '{0}' should not be 'abstract' - O método de TestCleanup "{0}" não deve ser "abstrato" - - - - TestCleanup method '{0}' should not be 'async void' - O método de TestCleanup "{0}" não deve ser "nulo assíncrono" - - - - TestCleanup method '{0}' should not be generic - O método de TestCleanup "{0}" não deve ser genérico - - - - TestCleanup method '{0}' should not be 'static' - O método de TestCleanup "{0}" não deve ser "estático" +— não ser "nulo assíncrono" +— não ser um método especial (finalizador, operador...). - - TestCleanup method '{0}' should be an 'ordinary' method - O método de TestCleanup "{0}" deve ser um método "comum" - - - - TestCleanup method '{0}' should be 'public' - O método de TestCleanup "{0}" deve ser "público" - - - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - O método de TestCleanup "{0}" deve retornar "nulo", "Tarefa" ou "ValueTask" + + TestCleanup method '{0}' signature is invalid + A assinatura do método TestCleanup "{0}" é inválida @@ -561,63 +436,28 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - Os métodos marcados com [TestInitialize] devem seguir o seguinte layout para serem válidos: -— deve ser "público" -— não deve ser "estático" -— não deve ser genérico +- not be 'async void' +- not be a special method (finalizer, operator...). + Os métodos marcados com [TestInitialize] devem seguir o seguinte layout para serem válidos: +— ser "público" +— não ser "estático" +— não ser genérico nem definido em uma classe genérica — não deve ser "abstrato" -— não deve aceitar nenhum parâmetro +— não aceitar nenhum parâmetro — o tipo de retorno deve ser "nulo", "Tarefa" ou "ValueTask" -— não deve ser "nulo assíncrono" -— não deve ser um método especial (finalizador, operador...). - - - - TestInitialize method '{0}' should not take any parameter - O método de TestInitialize "{0}" não deve aceitar nenhum parâmetro - - - - TestInitialize method '{0}' should not be 'abstract' - O método de TestInitialize "{0}" não deve ser "abstrato" - - - - TestInitialize method '{0}' should not be 'async void' - O método de TestInitialize "{0}" não deve ser "nulo assíncrono" - - - - TestInitialize method '{0}' should not be generic - O método de TestInitialize "{0}" não deve ser genérico +— não ser "nulo assíncrono" +— não ser um método especial (finalizador, operador...). - - TestInitialize method '{0}' should not be 'static' - O método de TestInitialize "{0}" não deve ser "estático" - - - - TestInitialize method '{0}' should be an 'ordinary' method - O método de TestInitialize "{0}" deve ser um método "comum" - - - - TestInitialize method '{0}' should be 'public' - O método de TestInitialize "{0}" deve ser "público" - - - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - O método de TestInitialize "{0}" deve retornar "nulo", "Tarefa" ou "ValueTask" + + TestInitialize method '{0}' signature is invalid + A assinatura do método TestInitialize "{0}" é inválida @@ -704,6 +544,31 @@ O método de teste não deve ser ignorado + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + O tipo contendo '[TestMethod]' deve ser marcado com '[TestClass]', caso contrário, o método de teste será ignorado silenciosamente. + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + A classe '{0}' contém métodos de teste e deve ser marcada com '[TestClass]' + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + O tipo contendo '[TestMethod]' deve ser marcado com '[TestClass]' + + + + Asynchronous test fixture methods do not require the 'Async' suffix + Os métodos de acessório de teste assíncronos não exigem o sufixo "Async" + + + + Asynchronous test methods do not require the 'Async' suffix + Os métodos de teste assíncronos não exigem o sufixo 'Async' + + [{0}] can only be set on methods marked with [TestMethod] [{0}] só pode ser definido em métodos marcados com [TestMethod] diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf index 5f4772de0d..3a3ed98288 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf @@ -4,61 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Чтобы методы, отмеченные [AssemblyCleanup], были допустимыми, они должны соответствовать следующему макету: -– метод должен быть общедоступным ("public") -– метод должен быть статическим ("static") -– метод не должен быть общим ("generic") -– метод не должен принимать никаких параметров -– метод должен возвращать значение типа "void", "Task" или "ValueTask" -– метод не должен быть асинхронным и не возвращающим значения ("async void") -– метод не должен быть специальным (метод завершения, оператор…). - - - - AssemblyCleanup method '{0}' should not take any parameter - Метод AssemblyCleanup "{0}" не должен принимать никаких параметров - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - Метод AssemblyCleanup "{0}" не может быть объявлен для универсального класса - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Метод AssemblyCleanup "{0}" должен возвращать значение "void", "Task" или "ValueTask" - - - - AssemblyCleanup method '{0}' should not be generic - Метод AssemblyCleanup "{0}" не должен быть общим ("generic") - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - Метод AssemblyCleanup "{0}" должен быть ординарным ("ordinary") - - - - AssemblyCleanup method '{0}' should be 'public' - Метод AssemblyCleanup "{0}" должен быть общедоступным ("public") - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Метод AssemblyCleanup "{0}" должен возвращать значение "void", "Task" или "ValueTask" +– должны быть "общедоступными" +– должны быть "статическими" +– не должны быть универсальным и не должны определяться в универсальном классе +– не должны принимать никаких параметров +– тип возвращаемого значения должен быть "void", "Task" или "ValueTask" +– не должны быть асинхронными и не возвращающими значения ("async void") +– не должны быть специальными методом (метод завершения, оператор...). - - AssemblyCleanup method '{0}' should be 'static' - Метод AssemblyCleanup "{0}" должен быть статическим ("static") + + AssemblyCleanup method '{0}' signature is invalid + Подпись метода AssemblyCleanup "{0}" недопустима @@ -68,66 +33,41 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Чтобы методы, отмеченные [AssemblyInitialize], были допустимыми, они должны соответствовать следующему макету: -– метод должен быть общедоступным ("public") -– метод должен быть статическим ("static") -– метод не должен быть общим ("generic") -– метод должен принимать один параметр типа "TestContext" -– метод должен возвращать значение типа "void", "Task" или "ValueTask" -– метод не должен быть асинхронным и не возвращающим значения ("async void") -– метод не должен быть специальным (метод завершения, оператор…). - - - - AssemblyInitialize method '{0}' can't be declared on a generic class - Метод AssemblyInitialize "{0}" не может быть объявлен для универсального класса - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Метод AssemblyInitialize "{0}" должен возвращать значение "void", "Task" или "ValueTask" - - - - AssemblyInitialize method '{0}' should not be generic - Метод AssemblyInitialize "{0}" не должен быть общим ("generic") - - - - AssemblyInitialize method '{0}' should be an 'ordinary' method - Метод AssemblyInitialize "{0}" должен быть ординарным ("ordinary") - - - - AssemblyInitialize method '{0}' should be 'public' - Метод AssemblyInitialize "{0}" должен быть общедоступным ("public") +– должны быть "общедоступными" +– должны быть "статическими" +– не должны быть универсальным и не должны определяться в универсальном классе +– должны принимать один параметр типа "TestContext" +– тип возвращаемого значения должен быть "void", "Task" или "ValueTask" +– не должны быть асинхронными и не возвращающими значения ("async void") +– не должны быть специальными методом (метод завершения, оператор...). - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Метод AssemblyInitialize "{0}" должен возвращать значение "void", "Task" или "ValueTask" + + AssemblyInitialize method '{0}' signature is invalid + Подпись метода AssemblyInitialize "{0}" недопустима - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - Метод AssemblyInitialize "{0}" должен принимать один параметр типа "TestContext" + + AssemblyInitialize methods should have valid layout + Методы AssemblyInitialize должны использовать допустимый макет - - AssemblyInitialize method '{0}' should be 'static' - Метод AssemblyInitialize "{0}" должен быть статическим ("static") + + Prefer adding an additional assertion that checks for null + Предпочитайте добавление дополнительного утверждения, проверяющего наличие значения NULL - - AssemblyInitialize methods should have valid layout - Методы AssemblyInitialize должны использовать допустимый макет + + Avoid conditional access in assertions + Избегайте условного доступа в утверждениях @@ -162,61 +102,26 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Чтобы методы, отмеченные [ClassCleanup], были допустимыми, они должны соответствовать следующему макету: -– метод должен быть общедоступным ("public") -– метод не должен быть статическим ("static") -– метод не должен быть общим ("generic") -– метод не должен принимать никаких параметров -– метод должен возвращать значение типа "void", "Task" или "ValueTask" -– метод не должен быть асинхронным и не возвращающим значения ("async void") -– метод не должен быть специальным (метод завершения, оператор…). - - - - ClassCleanup method '{0}' should not take any parameter - Метод ClassCleanup "{0}" не должен принимать никаких параметров - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - Метод ClassCleanup "{0}" не может быть объявлен для универсального класса, если не установлен режим InheritanceBehavior - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Метод ClassCleanup "{0}" должен возвращать значение "void", "Task" или "ValueTask" - - - - ClassCleanup method '{0}' should not be generic - Метод ClassCleanup "{0}" не должен быть общим ("generic") - - - - ClassCleanup method '{0}' should be an 'ordinary' method - Метод ClassCleanup "{0}" должен быть ординарным ("ordinary") - - - - ClassCleanup method '{0}' should be 'public' - Метод ClassCleanup "{0}" должен быть общедоступным ("public") - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Метод ClassCleanup "{0}" должен возвращать значение "void", "Task" или "ValueTask" +– должны быть "общедоступными" +– не должны быть "статическими" +– не должны быть универсальным и не должны определяться в универсальном классе +– не должны принимать никаких параметров +– тип возвращаемого значения должен быть "void", "Task" или "ValueTask" +– не должны быть асинхронными и не возвращающими значения ("async void") +– не должны быть специальными методом (метод завершения, оператор...). - - ClassCleanup method '{0}' should be 'static' - Метод ClassCleanup "{0}" должен быть статическим ("static") + + ClassCleanup method '{0}' signature is invalid + Подпись метода ClassCleanup "{0}" недопустима @@ -226,61 +131,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Чтобы методы, отмеченные [ClassInitialize], были допустимыми, они должны соответствовать следующему макету: -– метод должен быть общедоступным ("public") -– метод должен быть статическим ("static") -– метод не должен быть общим ("generic") -– метод должен принимать один параметр типа "TestContext" -– метод должен возвращать значение типа "void", "Task" или "ValueTask" -– метод не должен быть асинхронным и не возвращающим значения ("async void") -– метод не должен быть специальным (метод завершения, оператор…). - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - Метод ClassInitialize "{0}" не может быть объявлен для универсального класса, если не установлен режим InheritanceBehavior - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Метод ClassInitialize "{0}" должен возвращать значение "void", "Task" или "ValueTask" - - - - ClassInitialize method '{0}' should not be generic - Метод ClassInitialize "{0}" не должен быть общим ("generic") - - - - ClassInitialize method '{0}' should be an 'ordinary' method - Метод ClassInitialize "{0}" должен быть ординарным ("ordinary") - - - - ClassInitialize method '{0}' should be 'public' - Метод ClassInitialize "{0}" должен быть общедоступным ("public") - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Метод ClassInitialize "{0}" должен возвращать значение "void", "Task" или "ValueTask" - - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - Метод ClassInitialize "{0}" должен принимать один параметр типа "TestContext" +– должны быть "общедоступными" +– должны быть "статическими" +– не должны быть универсальным и не должны определяться в универсальном классе +– должны принимать один параметр типа "TestContext" +– тип возвращаемого значения должен быть "void", "Task" или "ValueTask" +– не должны быть асинхронными и не возвращающими значения ("async void") +– не должны быть специальными методом (метод завершения, оператор...). - - ClassInitialize method '{0}' should be 'static' - Метод ClassInitialize "{0}" должен быть статическим ("static") + + ClassInitialize method '{0}' signature is invalid + Подпись метода ClassInitialize "{0}" недопустима @@ -339,6 +209,21 @@ Не хранить TestContext в статическом элементе + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert Используйте "Assert.Fail" вместо утверждения с постоянным сбоем "Assert.{0}" @@ -349,6 +234,16 @@ Используйте "Assert.Fail" вместо утверждения с постоянным сбоем + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods Предпочитать конструкторы методам TestInitialize @@ -389,6 +284,21 @@ Предпочитать методы TestInitialize конструкторам + + Public methods should be test methods (marked with `[TestMethod]`). + Public methods should be test methods (marked with `[TestMethod]`). + + + + Public method '{0}' should be a test method + Public method '{0}' should be a test method + + + + Public methods should be test methods + Public methods should be test methods + + It's considered a good practice to have only test classes marked public in a test project. Рекомендуется использовать только тестовые классы, помеченные как общедоступные в тестовом проекте. @@ -452,63 +362,28 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - Чтобы методы, отмеченные [TestCleanup], были действительными, они должны соответствовать следующему методу: -– должно быть присвоено значение "public" -– должно быть присвоено значение, отличное от "static" -– должно быть присвоено значение, отличное от "generic" -– должно быть присвоено значение, отличное от "abstract" -– не должны присваиваться параметры +- not be 'async void' +- not be a special method (finalizer, operator...). + Чтобы методы, отмеченные [TestCleanup], были действительными, они должны соответствовать следующему макету: +– должны быть "общедоступными" +– не должны быть "статическими" +– не должны быть универсальным и не должны определяться в универсальном классе +– не должны быть "абстрактными" +– не должны принимать никаких параметров – тип возвращаемого значения должен быть "void", "Task" или "ValueTask" -– должно быть присвоено значение, отличное от "async void" -– это должен быть специальный метод (метод завершения, оператор...). - - - - TestCleanup method '{0}' should not take any parameter - Методу TestCleanup "{0}" не должны присваиваться параметры - - - - TestCleanup method '{0}' should not be 'abstract' - Методу TestCleanup "{0}" должно быть присвоено значение, отличное от "abstract" - - - - TestCleanup method '{0}' should not be 'async void' - Методу TestCleanup "{0}" не должно присваиваться значение "async void" - - - - TestCleanup method '{0}' should not be generic - Методу TestCleanup "{0}" не должно присваиваться значение "generic" - - - - TestCleanup method '{0}' should not be 'static' - Методу TestCleanup "{0}" не должно присваиваться значение "static" - - - - TestCleanup method '{0}' should be an 'ordinary' method - Методу TestCleanup "{0}" должно быть присвоено значение "ordinary" - - - - TestCleanup method '{0}' should be 'public' - Методу TestCleanup "{0}" должно быть присвоено значение "public" +– не должны быть асинхронными и не возвращающими значения ("async void") +– не должны быть специальными методом (метод завершения, оператор...). - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - Метод TestCleanup "{0}" должен возвращать значение "void", "Task" или "ValueTask" + + TestCleanup method '{0}' signature is invalid + Подпись метода TestCleanup "{0}" недопустима @@ -561,63 +436,28 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). Чтобы методы, отмеченные [TestInitialize], были допустимыми, они должны соответствовать следующему макету: -– должно быть присвоено значение "public" -– должно быть присвоено значение, отличное от "static" -– должно быть присвоено значение, отличное от "generic" -– должно быть присвоено значение, отличное от "abstract" -– не должны присваиваться параметры +– должны быть "общедоступными" +– не должны быть "статическими" +– не должны быть универсальным и не должны определяться в универсальном классе +– не должны быть "абстрактными" +– не должны принимать никаких параметров – тип возвращаемого значения должен быть "void", "Task" или "ValueTask" -– должно быть присвоено значение, отличное от "async void" -– это должен быть специальный метод (метод завершения, оператор...). +– не должны быть асинхронными и не возвращающими значения ("async void") +– не должны быть специальными методом (метод завершения, оператор...). - - TestInitialize method '{0}' should not take any parameter - Методу TestInitialize "{0}" не должны присваиваться параметры - - - - TestInitialize method '{0}' should not be 'abstract' - Методу TestInitialize "{0}" не должно присваиваться значение "abstract" - - - - TestInitialize method '{0}' should not be 'async void' - Методу TestInitialize "{0}" не должно присваиваться значение "async void" - - - - TestInitialize method '{0}' should not be generic - Методу TestInitialize "{0}" не должно присваиваться значение "generic" - - - - TestInitialize method '{0}' should not be 'static' - Методу TestInitialize "{0}" не должно присваиваться значение "static" - - - - TestInitialize method '{0}' should be an 'ordinary' method - Методу TestInitialize "{0}" должно быть присвоено значение "ordinary" - - - - TestInitialize method '{0}' should be 'public' - Методу TestInitialize "{0}" должно быть присвоено значение "public" - - - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - Метод TestInitialize "{0}" должен возвращать значение "void", "Task" или "ValueTask" + + TestInitialize method '{0}' signature is invalid + Подпись метода TestInitialize "{0}" недопустима @@ -704,6 +544,31 @@ Метод теста не должен игнорироваться + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + Class '{0}' contains test methods and should be marked with '[TestClass]' + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + Type containing '[TestMethod]' should be marked with '[TestClass]' + + + + Asynchronous test fixture methods do not require the 'Async' suffix + Методы асинхронных средств тестирования не требуют суффикса "Async". + + + + Asynchronous test methods do not require the 'Async' suffix + Асинхронные методы теста не требуют суффикса Async + + [{0}] can only be set on methods marked with [TestMethod] [{0}] можно задать только для методов с пометкой [TestMethod] diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf index 371e1218f4..6dc3b10499 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf @@ -4,61 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [AssemblyCleanup] ile işaretlenen yöntemlerin geçerli olması için aşağıdaki düzeni takip etmesi gerekir: -- 'public' olmalıdır -- 'static' olmalıdır -- genel olmamalıdır -- herhangi bir parametre almamalıdır +- 'public' olmalı +- 'static' olmalı +- genel olamaz veya genel bir sınıf üzerinde tanımlanamaz +- herhangi bir parametre alamaz - dönüş türü 'void', 'Task' veya 'ValueTask' olmalıdır -- 'async void' olmamalıdır -- özel bir yöntem (sonlandırıcı, işleç...) olmamalıdır. - - - - AssemblyCleanup method '{0}' should not take any parameter - '{0}' AssemblyCleanup yöntemi herhangi bir parametre almamalıdır - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - '{0}' AssemblyCleanup metodu genel bir sınıfta bildirilemez - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - '{0}' AssemblyCleanup yöntemi 'void', 'Task' veya 'ValueTask' döndürmelidir - - - - AssemblyCleanup method '{0}' should not be generic - '{0}' AssemblyCleanup yöntemi genel olmamalıdır - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - '{0}' AssemblyCleanup yöntemi 'ordinary' bir yöntem olmalıdır - - - - AssemblyCleanup method '{0}' should be 'public' - AssemblyCleanup yöntemi '{0}' 'public' olmalıdır - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - '{0}' AssemblyCleanup yöntemi 'void', 'Task' veya 'ValueTask' döndürmelidir +- 'async void' olamaz +- özel bir yöntem (sonlandırıcı, işleç...) olamaz. - - AssemblyCleanup method '{0}' should be 'static' - AssemblyCleanup yöntemi '{0}' 'static' olmalıdır + + AssemblyCleanup method '{0}' signature is invalid + AssemblyCleanup yöntemi '{0}' imzası geçersiz @@ -68,66 +33,41 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [AssemblyInitialize] ile işaretlenen yöntemlerin geçerli olması için aşağıdaki düzeni takip etmesi gerekir: -- 'public' olmalıdır -- 'static' olmalıdır -- genel olmamalıdır -- 'TestContext' türünden bir parametre almalıdır +- 'public' olmalı +- 'static' olmalı +- genel olamaz veya genel bir sınıf üzerinde tanımlanamaz +- 'TestContext' türünde tek bir parametre almalıdır - dönüş türü 'void', 'Task' veya 'ValueTask' olmalıdır -- 'async void' olmamalıdır -- özel bir yöntem (sonlandırıcı, işleç...) olmamalıdır. - - - - AssemblyInitialize method '{0}' can't be declared on a generic class - '{0}' AssemblyInitialize metodu genel bir sınıfta bildirilemez - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - '{0}' AssemblyInitialize yöntemi 'void', 'Task' veya 'ValueTask' döndürmelidir +- 'async void' olamaz +- özel bir yöntem (sonlandırıcı, işleç...) olamaz. - - AssemblyInitialize method '{0}' should not be generic - '{0}' AssemblyInitialize yöntemi genel olmamalıdır + + AssemblyInitialize method '{0}' signature is invalid + AssemblyInitialize yöntemi '{0}' imzası geçersiz - - AssemblyInitialize method '{0}' should be an 'ordinary' method - '{0}' AssemblyInitialize yöntemi 'ordinary' bir yöntem olmalıdır - - - - AssemblyInitialize method '{0}' should be 'public' - '{0}' AssemblyInitialize yöntemi 'public' olmalıdır - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - '{0}' AssemblyInitialize yöntemi 'void', 'Task' veya 'ValueTask' döndürmelidir - - - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - '{0}' AssemblyInitialize yöntemi, 'TestContext' türünde tek bir parametre almalıdır + + AssemblyInitialize methods should have valid layout + AssemblyInitialize yöntemleri geçerli bir düzene sahip olmalıdır - - AssemblyInitialize method '{0}' should be 'static' - AssemblyInitialize yöntemi '{0}' 'static' olmalıdır + + Prefer adding an additional assertion that checks for null + Null değerini denetlerken ek bir onaylama eklemeyi tercih et - - AssemblyInitialize methods should have valid layout - AssemblyInitialize yöntemleri geçerli bir düzene sahip olmalıdır + + Avoid conditional access in assertions + Onaylamalarda koşullu erişimden kaçın @@ -162,61 +102,26 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [ClassCleanup] ile işaretlenen yöntemlerin geçerli olması için aşağıdaki düzeni takip etmesi gerekir: -- 'public' olmalıdır -- 'static' olmamalıdır -- genel olmamalıdır -- herhangi bir parametre almamalıdır +- 'public' olmalı +- 'static' olamaz +- genel olamaz veya genel bir sınıf üzerinde tanımlanamaz +- herhangi bir parametre alamaz - dönüş türü 'void', 'Task' veya 'ValueTask' olmalıdır -- 'async void' olmamalıdır -- özel bir yöntem (sonlandırıcı, işleç...) olmamalıdır. - - - - ClassCleanup method '{0}' should not take any parameter - '{0}' ClassCleanup yöntemi herhangi bir parametre almamalıdır - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - '{0}' ClassCleanup metodu 'InheritanceBehavior' modu ayarlanmamış genel bir sınıfta bildirilemez - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - '{0}' ClassCleanup yöntemi 'void', 'Task' veya 'ValueTask' döndürmelidir - - - - ClassCleanup method '{0}' should not be generic - '{0}' ClassCleanup yöntemi genel olmamalıdır - - - - ClassCleanup method '{0}' should be an 'ordinary' method - '{0}' ClassCleanup yöntemi 'ordinary' bir yöntem olmalıdır - - - - ClassCleanup method '{0}' should be 'public' - '{0}' ClassCleanup yöntemi 'public' olmalıdır - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - '{0}' ClassCleanup yöntemi 'void', 'Task' veya 'ValueTask' döndürmelidir +- 'async void' olamaz +- özel bir yöntem (sonlandırıcı, işleç...) olamaz. - - ClassCleanup method '{0}' should be 'static' - '{0}' ClassCleanup yöntemi 'static' olmalıdır + + ClassCleanup method '{0}' signature is invalid + ClassCleanup yöntemi '{0}' imzası geçersiz @@ -226,61 +131,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [ClassInitialize] ile işaretlenen yöntemlerin geçerli olması için aşağıdaki düzeni takip etmesi gerekir: -- 'public' olmalıdır -- 'static' olmalıdır -- genel olmamalıdır -- 'TestContext' türünden bir parametre almalıdır +- 'public' olmalı +- 'static' olmalı +- genel olamaz veya genel bir sınıf üzerinde tanımlanamaz +- 'TestContext' türünde tek bir parametre almalıdır - dönüş türü 'void', 'Task' veya 'ValueTask' olmalıdır -- 'async void' olmamalıdır -- özel bir yöntem (sonlandırıcı, işleç...) olmamalıdır. - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - '{0}' ClassInitialize metodu 'InheritanceBehavior' modu ayarlanmamış genel bir sınıfta bildirilemez - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - '{0}' ClassInitialize yöntemi 'void', 'Task' veya 'ValueTask' döndürmelidir - - - - ClassInitialize method '{0}' should not be generic - '{0}' ClassInitialize yöntemi genel olmamalıdır +- 'async void' olamaz +- özel bir yöntem (sonlandırıcı, işleç...) olamaz. - - ClassInitialize method '{0}' should be an 'ordinary' method - '{0}' ClassInitialize yöntemi 'ordinary' bir yöntem olmalıdır - - - - ClassInitialize method '{0}' should be 'public' - ClassInitialize yöntemi '{0}' 'public' olmalıdır - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - '{0}' ClassInitialize yöntemi 'void', 'Task' veya 'ValueTask' döndürmelidir - - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - '{0}' ClassInitialize yöntemi, 'TestContext' türünde tek bir parametre almalıdır - - - - ClassInitialize method '{0}' should be 'static' - ClassInitialize yöntemi '{0}' 'static' olmalıdır + + ClassInitialize method '{0}' signature is invalid + ClassInitialize yöntemi '{0}' imzası geçersiz @@ -339,6 +209,21 @@ TestContext'i statik üyede depolama + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + 'System.ComponentModel.DescriptionAttribute'un testler bağlamında hiçbir etkisi yoktur ve muhtemelen bunun yerine 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' kullanmak istediniz. + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' kullanmayı mı düşündünüz? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + 'System.ComponentModel.DescriptionAttribute' test yöntemlerini etkilemez + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert Her zaman başarısız olan 'Assert.{0}' onaylaması yerine 'Assert.Fail' seçeneğini kullanın @@ -349,6 +234,16 @@ Her zaman başarısız olan onaylama yerine 'Assert.Fail' seçeneğini kullanın + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods Oluşturucuları TestInitialize yöntemlerine tercih et @@ -389,6 +284,21 @@ TestInitialize yöntemlerini oluşturuculara tercih et + + Public methods should be test methods (marked with `[TestMethod]`). + Public methods should be test methods (marked with `[TestMethod]`). + + + + Public method '{0}' should be a test method + Public method '{0}' should be a test method + + + + Public methods should be test methods + Public methods should be test methods + + It's considered a good practice to have only test classes marked public in a test project. Test projesinde yalnızca public olarak işaretlenmiş test sınıflarının olması iyi bir uygulama olarak kabul edilir. @@ -453,63 +363,28 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [TestCleanup] ile işaretlenen yöntemlerin geçerli olması için aşağıdaki düzeni takip etmesi gerekir: -- 'public' olmalıdır -- 'static' olmamalıdır -- genel olmamalıdır -- 'abstract' olmamalıdır -- herhangi bir parametre almamalıdır -- dönüş türü 'void', 'Task' veya 'ValueTask' olmalıdır -- 'async void' olmamalıdır -- özel bir yöntem (sonlandırıcı, işleç...) olmamalıdır. - - - - TestCleanup method '{0}' should not take any parameter - '{0}' TestCleanup yöntemi herhangi bir parametre almamalıdır - - - - TestCleanup method '{0}' should not be 'abstract' - '{0}' TestCleanup yöntemi 'abstract' olmamalıdır - - - - TestCleanup method '{0}' should not be 'async void' - '{0}' TestCleanup yöntemi 'async void' olmamalıdır - - - - TestCleanup method '{0}' should not be generic - '{0}' TestCleanup yöntemi genel olmamalıdır - - - - TestCleanup method '{0}' should not be 'static' - '{0}' TestCleanup yöntemi 'static' olmamalıdır +- 'public' olmalı +- 'static' olamaz +- genel olamaz veya genel bir sınıf üzerinde tanımlanamaz +- 'abstract' olamaz +- herhangi bir parametre almaz +- dönüş türü 'void', 'Task' veya 'ValueTask' olmalı +- 'async void' olamaz +- özel bir yöntem (sonlandırıcı, işleç...) olamaz. - - TestCleanup method '{0}' should be an 'ordinary' method - '{0}' TestCleanup yöntemi 'ordinary' bir yöntem olmalıdır - - - - TestCleanup method '{0}' should be 'public' - '{0}' TestCleanup yöntemi 'public' olmalıdır - - - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - '{0}' TestCleanup yöntemi 'void', 'Task' veya 'ValueTask' döndürmelidir + + TestCleanup method '{0}' signature is invalid + TestCleanup yöntemi '{0}' imzası geçersiz @@ -562,63 +437,28 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). +- not be 'async void' +- not be a special method (finalizer, operator...). [TestInitialize] ile işaretlenen yöntemlerin geçerli olması için aşağıdaki düzeni takip etmesi gerekir: -- 'public' olmalıdır -- 'static' olmamalıdır -- genel olmamalıdır -- 'abstract' olmamalıdır -- herhangi bir parametre almamalıdır -- dönüş türü 'void', 'Task' veya 'ValueTask' olmalıdır -- 'async void' olmamalıdır -- özel bir yöntem (sonlandırıcı, işleç...) olmamalıdır. +- 'public' olmalı +- 'static' olamaz +- genel olamaz veya genel bir sınıf üzerinde tanımlanamaz +- 'abstract' olamaz +- herhangi bir parametre almaz +- dönüş türü 'void', 'Task' veya 'ValueTask' olmalı +- 'async void' olamaz +- özel bir yöntem (sonlandırıcı, işleç...) olamaz. - - TestInitialize method '{0}' should not take any parameter - '{0}' TestCleanup yöntemi herhangi bir parametre almamalıdır - - - - TestInitialize method '{0}' should not be 'abstract' - '{0}' TestInitialize yöntemi 'abstract' olmamalıdır - - - - TestInitialize method '{0}' should not be 'async void' - '{0}' TestInitialize yöntemi 'async void' olmamalıdır - - - - TestInitialize method '{0}' should not be generic - '{0}' TestInitialize yöntemi genel olmamalıdır - - - - TestInitialize method '{0}' should not be 'static' - '{0}' TestInitialize yöntemi 'static' olmamalıdır - - - - TestInitialize method '{0}' should be an 'ordinary' method - '{0}' TestInitialize yöntemi 'ordinary' bir yöntem olmalıdır - - - - TestInitialize method '{0}' should be 'public' - '{0}' TestInitialize yöntemi 'public' olmalıdır - - - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - '{0}' TestCleanup yöntemi 'void', 'Task' veya 'ValueTask' döndürmelidir + + TestInitialize method '{0}' signature is invalid + TestInitialize yöntemi '{0}' imzası geçersiz @@ -706,6 +546,31 @@ Test metodu yoksayılmamalıdır + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + '[TestMethod]' içeren tür '[TestClass]' ile işaretlenmeli, aksi takdirde test yöntemi sessizce yoksayılır. + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + '{0}' sınıfı test yöntemleri içeriyor ve '[TestClass]' ile işaretlenmeli + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + '[TestMethod]' içeren tür '[TestClass]' ile işaretlenmelidir + + + + Asynchronous test fixture methods do not require the 'Async' suffix + Zaman uyumsuz test düzeni metotları için 'Async' soneki gerekmez + + + + Asynchronous test methods do not require the 'Async' suffix + Asenkron test metotları için 'Async' soneki gerekmez + + [{0}] can only be set on methods marked with [TestMethod] [{0}] yalnızca [TestMethod] ile işaretli yöntemlerde ayarlanabilir diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf index c17aa68ed1..97ed33cd7b 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf @@ -4,61 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - 标记有 [AssemblyCleanup] 的方法应遵循以下布局才有效: -- 它应为“public” -- 它应为“static” -- 它不应是泛型的 -- 它不应采用任何参数 -- 返回类型应为“void”、“Task”或“ValueTask” -- 它不应为“async void” -- 它不应是特殊方法(终结器、运算符...)。 - - - - AssemblyCleanup method '{0}' should not take any parameter - AssemblyCleanup 方法“{0}”不应采用任何参数 - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - 不能在泛型类上声明 AssemblyCleanup 方法“{0}” - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyCleanup 方法“{0}”应返回“void”、“Task”或“ValueTask” - - - - AssemblyCleanup method '{0}' should not be generic - AssemblyCleanup 方法“{0}”不应是泛型的 - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - AssemblyCleanup 方法“{0}”应为“普通”方法 - - - - AssemblyCleanup method '{0}' should be 'public' - AssemblyCleanup 方法“{0}”应是“public” - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyCleanup 方法“{0}”应返回“void”、“Task”或“ValueTask” +- not be 'async void' +- not be a special method (finalizer, operator...). + 标记有 [AssemblyCleanup] 的方法应遵循以下布局才会有效: +- 是 “public” +- 是 “static” +- 不是泛型的,也不是在泛型类上定义的 +- 不采用任何参数 +- 返回类型应为 "void"、"Task" 或 "ValueTask" +- 不是 “async void” +- 不是特殊方法(终结器、运算符...)。 - - AssemblyCleanup method '{0}' should be 'static' - AssemblyCleanup 方法“{0}”不应是“static” + + AssemblyCleanup method '{0}' signature is invalid + AssemblyCleanup 方法“{0}”签名无效 @@ -68,66 +33,41 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - 标记有 [AssemblyInitialize] 的方法应遵循以下布局才有效: -- 它应为“public” -- 它应为“static” -- 它不应是泛型的 -- 它应采用“TestContext”类型的一个参数 -- 返回类型应为“void”、“Task”或“ValueTask” -- 它不应为“async void” -- 它不应是特殊方法(终结器、运算符...)。 - - - - AssemblyInitialize method '{0}' can't be declared on a generic class - 不能在泛型类上声明 AssemblyInitialize 方法“{0}” - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyInitialize 方法“{0}”应返回“void”、“Task”或“ValueTask” - - - - AssemblyInitialize method '{0}' should not be generic - AssemblyInitialize 方法“{0}”不应是泛型的 - - - - AssemblyInitialize method '{0}' should be an 'ordinary' method - AssemblyInitialize 方法“{0}”应为“普通”方法 - - - - AssemblyInitialize method '{0}' should be 'public' - AssemblyInitialize 方法“{0}”不应是“public” +- not be 'async void' +- not be a special method (finalizer, operator...). + 标记有 [AssemblyInitialize] 的方法应遵循以下布局才会有效: +- 是 “public” +- 是 “static” +- 不是泛型的,也不是在泛型类上定义的 +- 采用 “TestContext” 类型的单个参数 +- 返回类型应为 "void"、"Task" 或 "ValueTask" +- 不是 “async void” +- 不是特殊方法(终结器、运算符...)。 - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyInitialize 方法“{0}”应返回“void”、“Task”或“ValueTask” + + AssemblyInitialize method '{0}' signature is invalid + AssemblyInitialize 方法“{0}”签名无效 - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - AssemblyInitialize 方法“{0}”应采用类型为“TestContext”的单个参数 + + AssemblyInitialize methods should have valid layout + AssemblyInitialize 方法应具有有效的布局 - - AssemblyInitialize method '{0}' should be 'static' - AssemblyInitialize 方法“{0}”不应是“static” + + Prefer adding an additional assertion that checks for null + 首选添加检查 null 的其他断言 - - AssemblyInitialize methods should have valid layout - AssemblyInitialize 方法应具有有效的布局 + + Avoid conditional access in assertions + 避免断言中的条件访问 @@ -162,61 +102,26 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - 标记有 [ClassCleanup] 的方法应遵循以下布局才有效: -- 它应为“public” -- 它不应为“static” -- 它不应是泛型的 -- 它不应采用任何参数 -- 返回类型应为“void”、“Task”或“ValueTask” -- 它不应为“async void” -- 它不应是特殊方法(终结器、运算符...)。 - - - - ClassCleanup method '{0}' should not take any parameter - ClassCleanup 方法“{0}”不应采用任何参数 - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - 如果未设置 `InheritanceBehavior` 模式,则无法在泛型类上声明 ClassCleanup 方法“{0}” - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassCleanup 方法“{0}”应返回“void”、“Task” 或“ValueTask” - - - - ClassCleanup method '{0}' should not be generic - ClassCleanup 方法“{0}”不应是泛型的 - - - - ClassCleanup method '{0}' should be an 'ordinary' method - ClassCleanup 方法“{0}”应为一种“普通”方法 - - - - ClassCleanup method '{0}' should be 'public' - ClassCleanup 方法“{0}”应为“public” - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassCleanup 方法“{0}”应返回“void”、“Task” 或“ValueTask” +- not be 'async void' +- not be a special method (finalizer, operator...). + 标记有 [ClassCleanup] 的方法应遵循以下布局才会有效: +- 是 “public” +- 不是 “static” +- 不是泛型的,也不是在泛型类上定义的 +- 不采用任何参数 +- 返回类型应为 "void"、"Task" 或 "ValueTask" +- 不是 “async void” +- 不是特殊方法(终结器、运算符...)。 - - ClassCleanup method '{0}' should be 'static' - ClassCleanup 方法“{0}”应为“static” + + ClassCleanup method '{0}' signature is invalid + ClassCleanup 方法“{0}”签名无效 @@ -226,61 +131,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - 标记有 [ClassInitialize] 的方法应遵循以下布局才有效: -- 它应为“public” -- 它应为“static” -- 它不应是泛型的 -- 它应采用“TestContext”类型的一个参数 -- 返回类型应为“void”、“Task”或“ValueTask” -- 它不应为“async void” -- 它不应是特殊方法(终结器、运算符...)。 - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - 如果未设置 `InheritanceBehavior` 模式,则无法在泛型类上声明 ClassInitialize 方法“{0}” - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassInitialize 方法“{0}”应返回“void”、“Task”或“ValueTask” - - - - ClassInitialize method '{0}' should not be generic - ClassInitialize 方法“{0}”不应是泛型的 - - - - ClassInitialize method '{0}' should be an 'ordinary' method - ClassInitialize 方法“{0}”应为“普通”方法 - - - - ClassInitialize method '{0}' should be 'public' - ClassInitialize 方法“{0}”不应是“public” - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassInitialize 方法“{0}”应返回“void”、“Task”或“ValueTask” - - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - ClassInitialize 方法“{0}”应采用类型为“TestContext”的单个参数 +- not be 'async void' +- not be a special method (finalizer, operator...). + 标记有 [ClassInitialize] 的方法应遵循以下布局才会有效: +- 是 “public” +- 是 “static” +- 不是泛型的,也不是在泛型类上定义的 +- 采用 “TestContext” 类型的单个参数 +- 返回类型应为 "void"、"Task" 或 "ValueTask" +- 不是 “async void” +- 不是特殊方法(终结器、运算符...)。 - - ClassInitialize method '{0}' should be 'static' - ClassInitialize 方法“{0}”不应是“static” + + ClassInitialize method '{0}' signature is invalid + ClassInitialize 方法“{0}”签名无效 @@ -339,6 +209,21 @@ 不要将 TestContext 存储在静态成员中 + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + “System.ComponentModel.DescriptionAttribute” 在测试上下文中无效,你可能想要改用 “Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute”。 + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + 是否打算使用 “Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute”? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + “System.ComponentModel.DescriptionAttribute” 对测试方法没有影响 + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert 使用 “Assert.Fail” 而不是始终失败的 “Assert.{0}” 断言 @@ -349,6 +234,16 @@ 使用 “Assert.Fail” 而不是始终失败的断言 + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods 首选构造函数而不是 TestInitialize 方法 @@ -389,6 +284,21 @@ 首选 TestInitialize 方法而不是构造函数 + + Public methods should be test methods (marked with `[TestMethod]`). + Public methods should be test methods (marked with `[TestMethod]`). + + + + Public method '{0}' should be a test method + Public method '{0}' should be a test method + + + + Public methods should be test methods + Public methods should be test methods + + It's considered a good practice to have only test classes marked public in a test project. 在测试项目中仅将测试类标记为“公开”是一种不错的做法。 @@ -452,63 +362,28 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - 标记有 [TestCleanup] 的方法应遵循以下布局才有效: -- 它应为 "public" -- 它不应为 "static" -- 它不应是泛型的 -- 它不应为 "abstract" -- 它不应采用任何参数 +- not be 'async void' +- not be a special method (finalizer, operator...). + 标记有 [TestCleanup] 的方法应遵循以下布局才会有效: +- 是 “public” +- 不是 “static” +- 不是泛型的,也不是在泛型类上定义的 +- 不是 “abstract” +- 不采用任何参数 - 返回类型应为 "void"、"Task" 或 "ValueTask" -- 它不应为 "async void" -- 它不应是特殊方法(终结器、运算符...)。 - - - - TestCleanup method '{0}' should not take any parameter - TestCleanup 方法“{0}”不应采用任何参数 - - - - TestCleanup method '{0}' should not be 'abstract' - TestCleanup 方法“{0}”不应为 "abstract" - - - - TestCleanup method '{0}' should not be 'async void' - TestCleanup 方法“{0}”不应为 "async void" +- 不是 “async void” +- 不是特殊方法(终结器、运算符...)。 - - TestCleanup method '{0}' should not be generic - TestCleanup 方法“{0}”不应是泛型的 - - - - TestCleanup method '{0}' should not be 'static' - TestCleanup 方法“{0}”不应为 "static" - - - - TestCleanup method '{0}' should be an 'ordinary' method - TestCleanup 方法“{0}”应为一种“普通”方法 - - - - TestCleanup method '{0}' should be 'public' - TestCleanup 方法“{0}”应为 "public" - - - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - TestCleanup 方法“{0}”应返回 "void"、"Task" 或 "ValueTask" + + TestCleanup method '{0}' signature is invalid + TestCleanup 方法“{0}”签名无效 @@ -561,63 +436,28 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - 标记有 [TestInitialize] 的方法应遵循以下布局才有效: -- 它应为 "public" -- 它不应为 "static" -- 它不应是泛型的 -- 它不应为 "abstract" -- 它不应采用任何参数 +- not be 'async void' +- not be a special method (finalizer, operator...). + 标记有 [TestInitialize] 的方法应遵循以下布局才会有效: +- 是 “public” +- 不是 “static” +- 不是泛型的,也不是在泛型类上定义的 +- 不是 “abstract” +- 不采用任何参数 - 返回类型应为 "void"、"Task" 或 "ValueTask" -- 它不应为 "async void" -- 它不应是特殊方法(终结器、运算符...)。 - - - - TestInitialize method '{0}' should not take any parameter - TestInitialize 方法“{0}”不应采用任何参数 - - - - TestInitialize method '{0}' should not be 'abstract' - TestInitialize 方法“{0}”不应为 "abstract" - - - - TestInitialize method '{0}' should not be 'async void' - TestInitialize 方法“{0}”不应为 "async void" - - - - TestInitialize method '{0}' should not be generic - TestInitialize 方法“{0}”不应是泛型的 - - - - TestInitialize method '{0}' should not be 'static' - TestInitialize 方法“{0}”不应为 "static" +- 不是 “async void” +- 不是特殊方法(终结器、运算符...)。 - - TestInitialize method '{0}' should be an 'ordinary' method - TestInitialize 方法“{0}”应为一种“普通”方法 - - - - TestInitialize method '{0}' should be 'public' - TestInitialize 方法“{0}”应为 "public" - - - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - TestInitialize 方法“{0}”应返回 "void"、"Task" 或 "ValueTask" + + TestInitialize method '{0}' signature is invalid + TestInitialize 方法“{0}”签名无效 @@ -704,6 +544,31 @@ 不应忽略测试方法 + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + 应使用“[TestClass]”标记包含“[TestMethod]”的类型,否则将以静默方式忽略该测试方法。 + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + 类“{0}”包含测试方法,应使用“[TestClass]”进行标记 + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + 应使用“[TestClass]”标记包含“[TestMethod]”的类型 + + + + Asynchronous test fixture methods do not require the 'Async' suffix + 异步测试固定例程方法不需要 "Async" 后缀 + + + + Asynchronous test methods do not require the 'Async' suffix + 异步测试方法不需要 "Async" 后缀 + + [{0}] can only be set on methods marked with [TestMethod] 只能对标记了 [TestMethod] 的方法设置 [{0}] diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf index 9f4f424e6b..c982e6b28d 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf @@ -4,61 +4,26 @@ Methods marked with [AssemblyCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - 標示為 [AssemblyCleanup] 的方法應該遵循下列配置才能有效: -- 其應為 'public' -- 其應為 'static' -- 其不應為泛型 -- 其不應接受任何參數 -- 傳回類型應為 'void'、'Task' 或 'ValueTask' -- 其不應為 'async void' -- 其不應為特殊方法 (完成項、運算子...)。 - - - - AssemblyCleanup method '{0}' should not take any parameter - AssemblyCleanup 方法 '{0}' 不應該接受任何參數 - - - - AssemblyCleanup method '{0}' can't be declared on a generic class - AssemblyCleanup 方法 '{0}' 不能在泛型類上宣告 - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyCleanup 方法 '{0}' 應傳回 'void'、'Task' 或 'ValueTask' - - - - AssemblyCleanup method '{0}' should not be generic - AssemblyCleanup 方法 '{0}' 不應為泛型 - - - - AssemblyCleanup method '{0}' should be an 'ordinary' method - AssemblyCleanup 方法 '{0}' 應為 'ordinary' 方法 - - - - AssemblyCleanup method '{0}' should be 'public' - AssemblyCleanup 方法 '{0}' 應為 'public' - - - - AssemblyCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyCleanup 方法 '{0}' 應傳回 'void'、'Task' 或 'ValueTask' +- not be 'async void' +- not be a special method (finalizer, operator...). + 標示為 [AssemblyCleanup] 的方法應該遵循下列配置才能有效: +- 為 'public' +- 為 'static' +- 非為泛型,也不是在泛型類別上所定義 +- 不接受任何參數 +- 傳回型別應為 'void'、'Task' 或 'ValueTask' +- 非為 'async void' +- 非為特殊方法 (完成項、運算子...)。 - - AssemblyCleanup method '{0}' should be 'static' - AssemblyCleanup 方法 '{0}' 應為 'static' + + AssemblyCleanup method '{0}' signature is invalid + AssemblyCleanup 方法 '{0}' 簽章無效 @@ -68,66 +33,41 @@ Methods marked with [AssemblyInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - 標示為 [AssemblyInitialize] 的方法應該遵循下列配置才能有效: -- 其應為 'public' -- 其應為 'static' -- 其不應為泛型 -- 其應該接受類型為 'TestContext' 的一個參數 -- 傳回類型應為 'void'、'Task' 或 'ValueTask' -- 其不應為 'async void' -- 其不應為特殊方法 (完成項、運算子...)。 - - - - AssemblyInitialize method '{0}' can't be declared on a generic class - AssemblyInitialize 方法 '{0}' 不能在泛型類上宣告 - - - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyInitialize 方法 '{0}' 應傳回 'void'、'Task' 或 'ValueTask' - - - - AssemblyInitialize method '{0}' should not be generic - AssemblyInitialize 方法 '{0}' 不應為泛型 - - - - AssemblyInitialize method '{0}' should be an 'ordinary' method - AssemblyInitialize 方法 '{0}' 應為 'ordinary' 方法 +- not be 'async void' +- not be a special method (finalizer, operator...). + 標示為 [AssemblyInitialize] 的方法應該遵循下列配置才能有效: +- 為 'public' +- 為 'static' +- 非為泛型也不是在泛型類別上所定義 +- 接受型別為 'TestContext' 的單一參數 +- 傳回型別應為 'void'、'Task' 或 'ValueTask' +- 非為 'async void' +- 非為特殊方法 (完成項、運算子...)。 - - AssemblyInitialize method '{0}' should be 'public' - AssemblyInitialize 方法 '{0}' 應為 'public' + + AssemblyInitialize method '{0}' signature is invalid + AssemblyInitialize 方法 '{0}' 簽章無效 - - AssemblyInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - AssemblyInitialize 方法 '{0}' 應傳回 'void'、'Task' 或 'ValueTask' + + AssemblyInitialize methods should have valid layout + AssemblyInitialize 方法應該具有有效的配置 - - AssemblyInitialize method '{0}' should take a single parameter of type 'TestContext' - AssemblyInitialize 方法 '{0}' 應該接受類型 'TestContext' 的單一參數 + + Prefer adding an additional assertion that checks for null + 偏好新增檢查 Null 的其他判斷提示 - - AssemblyInitialize method '{0}' should be 'static' - AssemblyInitialize 方法 '{0}' 應為 'static' - - - - AssemblyInitialize methods should have valid layout - AssemblyInitialize 方法應該具有有效的配置 + + Avoid conditional access in assertions + 避免判斷提示中的條件式存取 @@ -162,61 +102,26 @@ Methods marked with [ClassCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not 'static' -- it should not be generic -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - 標示為 [ClassCleanup] 的方法應該遵循下列配置才能有效: -- 其應為 'public' -- 其不應為 'static' -- 其不應為泛型 -- 其不應接受任何參數 -- 傳回類型應為 'void'、'Task' 或 'ValueTask' -- 其不應為 'async void' -- 其不應為特殊方法 (完成項、運算子...)。 - - - - ClassCleanup method '{0}' should not take any parameter - ClassCleanup 方法 '{0}' 不應該接受任何參數 - - - - ClassCleanup method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - 未設定 `InheritanceBehavior` 模式,則 ClassCleanup 方法 '{0}' 不能在泛型類別上宣告 - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassCleanup 方法 '{0}' 應傳回 'void'、'Task' 或 'ValueTask' - - - - ClassCleanup method '{0}' should not be generic - ClassCleanup 方法 '{0}' 不應為泛型 - - - - ClassCleanup method '{0}' should be an 'ordinary' method - ClassCleanup 方法 '{0}' 應為 'ordinary' 方法 - - - - ClassCleanup method '{0}' should be 'public' - ClassCleanup 方法 '{0}' 應為 'public' - - - - ClassCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassCleanup 方法 '{0}' 應傳回 'void'、'Task' 或 'ValueTask' +- not be 'async void' +- not be a special method (finalizer, operator...). + 標示為 [ClassCleanup] 的方法應該遵循下列配置才能有效: +- 為 'public' +- 非為 'static' +- 非為泛型,也不是在泛型類別上所定義 +- 不接受任何參數 +- 傳回型別應為 'void'、'Task' 或 'ValueTask' +- 非為 'async void' +- 非為特殊方法 (完成項、運算子...)。 - - ClassCleanup method '{0}' should be 'static' - ClassCleanup 方法 '{0}' 應為 'static' + + ClassCleanup method '{0}' signature is invalid + ClassCleanup 方法 '{0}' 簽章無效 @@ -226,61 +131,26 @@ Methods marked with [ClassInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should be 'static' -- it should not be generic -- it should take one parameter of type 'TestContext' +- be 'public' +- be 'static' +- not be generic nor be defined on a generic class +- take a single parameter of type 'TestContext' - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - 標示為 [ClassInitialize] 的方法應該遵循下列配置才能有效: -- 其應為 'public' -- 其應為 'static' -- 其不應為泛型 -- 其應該接受類型為 'TestContext' 的一個參數 -- 傳回類型應為 'void'、'Task' 或 'ValueTask' -- 其不應為 'async void' -- 其不應為特殊方法 (完成項、運算子...)。 - - - - ClassInitialize method '{0}' can't be declared on a generic class without the `InheritanceBehavior` mode is set - 未設定 `InheritanceBehavior` 模式,則 ClassInitialize 方法 '{0}' 不能在泛型類別上宣告 - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassInitialize 方法 '{0}' 應傳回 'void'、'Task' 或 'ValueTask' - - - - ClassInitialize method '{0}' should not be generic - ClassInitialize 方法 '{0}' 不應為泛型 - - - - ClassInitialize method '{0}' should be an 'ordinary' method - ClassInitialize 方法 '{0}' 應為 'ordinary' 方法 - - - - ClassInitialize method '{0}' should be 'public' - ClassInitialize 方法 '{0}' 應為 'public' - - - - ClassInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - ClassInitialize 方法 '{0}' 應傳回 'void'、'Task' 或 'ValueTask' - - - - ClassInitialize method '{0}' should take a single parameter of type 'TestContext' - ClassInitialize 方法 '{0}' 應該接受類型 'TestContext' 的單一參數 +- not be 'async void' +- not be a special method (finalizer, operator...). + 標示為 [ClassInitialize] 的方法應該遵循下列配置才能有效: +- 為 'public' +- 為 'static' +- 非為泛型也不是在泛型類別上所定義 +- 接受型別為 'TestContext' 的單一參數 +- 傳回型別應為 'void'、'Task' 或 'ValueTask' +- 非為 'async void' +- 非為特殊方法 (完成項、運算子...)。 - - ClassInitialize method '{0}' should be 'static' - ClassInitialize 方法 '{0}' 應為 'static' + + ClassInitialize method '{0}' signature is invalid + ClassInitialize 方法 '{0}' 簽章無效 @@ -339,6 +209,21 @@ 請勿將 TestContext 儲存在靜態成員中 + + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + 'System.ComponentModel.DescriptionAttribute' has no effect in the context of tests and you likely wanted to use 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute' instead. + + + + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + Did you mean to be using 'Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute'? + + + + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + 'System.ComponentModel.DescriptionAttribute' has no effect on test methods + + Use 'Assert.Fail' instead of an always-failing 'Assert.{0}' assert 使用 'Assert.Fail',而不是一直失敗的 'Assert.{0}' 聲明 @@ -349,6 +234,16 @@ 使用 'Assert.Fail',而不是一直失敗的聲明 + + Review or remove the assertion as its condition is known to be always true + Review or remove the assertion as its condition is known to be always true + + + + Assertion condition is always true + Assertion condition is always true + + Prefer constructors over TestInitialize methods 偏好建構函式勝於 TestInitialize 方法 @@ -389,6 +284,21 @@ 偏好 TestInitialize 方法勝於建構函式 + + Public methods should be test methods (marked with `[TestMethod]`). + Public methods should be test methods (marked with `[TestMethod]`). + + + + Public method '{0}' should be a test method + Public method '{0}' should be a test method + + + + Public methods should be test methods + Public methods should be test methods + + It's considered a good practice to have only test classes marked public in a test project. 在測試專案中僅將測試類別標記為公開被認為是一種很好的做法。 @@ -452,63 +362,28 @@ Methods marked with [TestCleanup] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic or be defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - 標示為 [TestCleanup] 的方法應該遵循下列配置才能有效: -- 其應為 'public' -- 其不應為 'static' -- 其不應為泛型 -- 其不應為 'abstract' -- 其不應接受任何參數 +- not be 'async void' +- not be a special method (finalizer, operator...). + 標示為 [TestCleanup] 的方法應該遵循下列配置才能有效: +- 為 'public' +- 非為 'static' +- 非為泛型或不是在泛型類別上所定義 +- 非為 'abstract' +- 不接受任何參數 - 傳回型別應為 'void'、'Task' 或 'ValueTask' -- 其不應為 'async void' -- 其不應為特殊方法 (完成項、運算子...)。 +- 非為 'async void' +- 非為特殊方法 (完成項、運算子...)。 - - TestCleanup method '{0}' should not take any parameter - TestCleanup 方法 '{0}' 不應該接受任何參數 - - - - TestCleanup method '{0}' should not be 'abstract' - TestCleanup 方法 '{0}' 不應為 'abstract' - - - - TestCleanup method '{0}' should not be 'async void' - TestCleanup 方法 '{0}' 不應為 'async void' - - - - TestCleanup method '{0}' should not be generic - TestCleanup 方法 '{0}' 不應為泛型 - - - - TestCleanup method '{0}' should not be 'static' - TestCleanup 方法 '{0}' 不應為 'static' - - - - TestCleanup method '{0}' should be an 'ordinary' method - TestCleanup 方法 '{0}' 應為 'ordinary' 方法 - - - - TestCleanup method '{0}' should be 'public' - TestCleanup 方法 '{0}' 應為 'public' - - - - TestCleanup method '{0}' should return 'void', 'Task' or 'ValueTask' - TestCleanup 方法 '{0}' 應傳回 'void'、'Task' 或 'ValueTask' + + TestCleanup method '{0}' signature is invalid + TestCleanup 方法 '{0}' 簽章無效 @@ -561,63 +436,28 @@ Methods marked with [TestInitialize] should follow the following layout to be valid: -- it should be 'public' -- it should not be 'static' -- it should not be generic -- it should not be 'abstract' -- it should not take any parameter +- be 'public' +- not be 'static' +- not be generic nor defined on a generic class +- not be 'abstract' +- not take any parameter - return type should be 'void', 'Task' or 'ValueTask' -- it should not be 'async void' -- it should not be a special method (finalizer, operator...). - 標示為 [TestInitialize] 的方法應該遵循下列配置才能有效: -- 其應為 'public' -- 其不應為 'static' -- 其不應為泛型 -- 其不應為 'abstract' -- 其不應接受任何參數 +- not be 'async void' +- not be a special method (finalizer, operator...). + 標示為 [TestInitialize] 的方法應該遵循下列配置才能有效: +- 為 'public' +- 非為 'static' +- 非為泛型,也不是在泛型類別上所定義 +- 非為 'abstract' +- 不接受任何參數 - 傳回型別應為 'void'、'Task' 或 'ValueTask' -- 其不應為 'async void' -- 其不應為特殊方法 (完成項、運算子...)。 - - - - TestInitialize method '{0}' should not take any parameter - TestInitialize 方法 '{0}' 不應使用任何參數 - - - - TestInitialize method '{0}' should not be 'abstract' - TestInitialize 方法 '{0}' 不應為 'abstract' - - - - TestInitialize method '{0}' should not be 'async void' - TestInitialize 方法 '{0}' 不應為 'async void' - - - - TestInitialize method '{0}' should not be generic - TestInitialize 方法 '{0}' 不應為泛型 - - - - TestInitialize method '{0}' should not be 'static' - TestInitialize 方法 '{0}' 不應為 'static' - - - - TestInitialize method '{0}' should be an 'ordinary' method - TestInitialize 方法 '{0}' 應為 'ordinary' 方法 +- 非為 'async void' +- 非為特殊方法 (完成項、運算子...)。 - - TestInitialize method '{0}' should be 'public' - TestInitialize 方法 '{0}' 不應為 'public' - - - - TestInitialize method '{0}' should return 'void', 'Task' or 'ValueTask' - TestInitialize 方法 '{0}' 應傳回 'void'、'Task' 或 'ValueTask' + + TestInitialize method '{0}' signature is invalid + TestInitialize 方法 '{0}' 簽章無效 @@ -704,6 +544,31 @@ 不應略過測試方法 + + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + Type contaning '[TestMethod]' should be marked with '[TestClass]', otherwise the test method will be silently ignored. + + + + Class '{0}' contains test methods and should be marked with '[TestClass]' + Class '{0}' contains test methods and should be marked with '[TestClass]' + + + + Type containing '[TestMethod]' should be marked with '[TestClass]' + Type containing '[TestMethod]' should be marked with '[TestClass]' + + + + Asynchronous test fixture methods do not require the 'Async' suffix + 非同步測試固件方法不需要 'Async' 尾碼 + + + + Asynchronous test methods do not require the 'Async' suffix + 非同步測試方法不需要 'Async' 尾碼 + + [{0}] can only be set on methods marked with [TestMethod] [{0}] 只能在標示為 [TestMethod] 的方法上設定 diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 59fd384fa2..848f42e441 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -5,4 +5,5 @@ enable + diff --git a/src/Package/MSTest.Sdk/MSTest.Sdk.csproj b/src/Package/MSTest.Sdk/MSTest.Sdk.csproj index 4e2ecf5e53..9df47c1e9a 100644 --- a/src/Package/MSTest.Sdk/MSTest.Sdk.csproj +++ b/src/Package/MSTest.Sdk/MSTest.Sdk.csproj @@ -35,7 +35,7 @@ - <_TemplateProperties>MSTestEngineVersion=$(MSTestEngineVersion);MSTestVersion=$(Version);MicrosoftTestingPlatformVersion=$(MicrosoftTestingPlatformVersion);MicrosoftNETTestSdkVersion=$(MicrosoftNETTestSdkVersion);MicrosoftTestingExtensionsCodeCoverageVersion=$(MicrosoftTestingExtensionsCodeCoverageVersion);MicrosoftPlaywrightVersion=$(MicrosoftPlaywrightVersion);AspireHostingTestingVersion=$(AspireHostingTestingVersion) + <_TemplateProperties>MSTestEngineVersion=$(MSTestEngineVersion);MSTestVersion=$(Version);MicrosoftTestingPlatformVersion=$(Version.Replace('$(VersionPrefix)', '$(TestingPlatformVersionPrefix)'));MicrosoftTestingEntrepriseExtensionsVersion=$(MicrosoftTestingInternalFrameworkVersion);MicrosoftNETTestSdkVersion=$(MicrosoftNETTestSdkVersion);MicrosoftTestingExtensionsCodeCoverageVersion=$(MicrosoftTestingExtensionsCodeCoverageVersion);MicrosoftPlaywrightVersion=$(MicrosoftPlaywrightVersion);AspireHostingTestingVersion=$(AspireHostingTestingVersion) true - $(MicrosoftTestingExtensionsCommonVersion) + $(MicrosoftTestingEntrepriseExtensionsVersion) true - $(MicrosoftTestingExtensionsCommonVersion) + $(MicrosoftTestingEntrepriseExtensionsVersion) true @@ -28,20 +28,29 @@ - - - - + + + + + + - - - - - - + + + + + + diff --git a/src/Package/MSTest.Sdk/Sdk/Runner/NativeAOT.targets b/src/Package/MSTest.Sdk/Sdk/Runner/NativeAOT.targets index a765c99fdc..5f9fe83b83 100644 --- a/src/Package/MSTest.Sdk/Sdk/Runner/NativeAOT.targets +++ b/src/Package/MSTest.Sdk/Sdk/Runner/NativeAOT.targets @@ -8,23 +8,23 @@ - + - - - - + + + + - - + + - + diff --git a/src/Package/MSTest.Sdk/Sdk/Sdk.props.template b/src/Package/MSTest.Sdk/Sdk/Sdk.props.template index 4506356bd1..d5a76d1f15 100644 --- a/src/Package/MSTest.Sdk/Sdk/Sdk.props.template +++ b/src/Package/MSTest.Sdk/Sdk/Sdk.props.template @@ -5,7 +5,7 @@ - false + false false false @@ -16,6 +16,7 @@ ${MicrosoftPlaywrightVersion} ${MicrosoftTestingExtensionsCodeCoverageVersion} ${MicrosoftTestingPlatformVersion} + ${MicrosoftTestingEntrepriseExtensionsVersion} diff --git a/src/Package/MSTest.Sdk/Sdk/VSTest/VSTest.targets b/src/Package/MSTest.Sdk/Sdk/VSTest/VSTest.targets index fbec4b4afa..63cd0fb644 100644 --- a/src/Package/MSTest.Sdk/Sdk/VSTest/VSTest.targets +++ b/src/Package/MSTest.Sdk/Sdk/VSTest/VSTest.targets @@ -2,10 +2,10 @@ - - - - + + + + + $(TestingPlatformVersionPrefix) + + + + + true + true + true + + + + + Microsoft test testing unittest unittesting unit-testing tdd + + + diff --git a/src/Platform/Directory.Build.targets b/src/Platform/Directory.Build.targets new file mode 100644 index 0000000000..d7a29489b3 --- /dev/null +++ b/src/Platform/Directory.Build.targets @@ -0,0 +1,17 @@ + + + + + + + + $(CommonPackageTags) + PACKAGE.md + + + + + + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.CrashDump/BannedSymbols.txt new file mode 100644 index 0000000000..ea8617fcb0 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/BannedSymbols.txt @@ -0,0 +1,10 @@ +T:System.ArgumentNullException; Use 'ArgumentGuard' instead +P:System.DateTime.Now; Use 'IClock' instead +P:System.DateTime.UtcNow; Use 'IClock' instead +M:System.Threading.Tasks.Task.Run(System.Action); Use 'ITask' instead +M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask' instead +M:System.Threading.Tasks.Task.WhenAll(System.Collections.Generic.IEnumerable{System.Threading.Tasks.Task}); Use 'ITask' instead +M:System.String.IsNullOrEmpty(System.String); Use 'RoslynString.IsNullOrEmpty' instead +M:System.String.IsNullOrWhiteSpace(System.String); Use 'RoslynString.IsNullOrWhiteSpace' instead +M:System.Diagnostics.Debug.Assert(System.Boolean); Use 'RoslynDebug.Assert' instead +M:System.Diagnostics.Debug.Assert(System.Boolean,System.String); Use 'RoslynDebug.Assert' instead diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpCommandLineOptions.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpCommandLineOptions.cs new file mode 100644 index 0000000000..cb003f5027 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpCommandLineOptions.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if USE_TRX_NAMESPACE +namespace Microsoft.Testing.Extensions.TrxReport; +#else +namespace Microsoft.Testing.Extensions.Diagnostics; +#endif + +internal static class CrashDumpCommandLineOptions +{ + public const string CrashDumpOptionName = "crashdump"; + public const string CrashDumpFileNameOptionName = "crashdump-filename"; + public const string CrashDumpTypeOptionName = "crashdump-type"; +} diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpCommandLineProvider.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpCommandLineProvider.cs new file mode 100644 index 0000000000..7bed8e2ec8 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpCommandLineProvider.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.Diagnostics.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Extensions.Diagnostics; + +internal sealed class CrashDumpCommandLineProvider : ICommandLineOptionsProvider +{ + private static readonly string[] DumpTypeOptions = ["Mini", "Heap", "Triage", "Full"]; + + public string Uid => nameof(CrashDumpCommandLineProvider); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => CrashDumpResources.CrashDumpDisplayName; + + public string Description => CrashDumpResources.CrashDumpDescription; + + public Task IsEnabledAsync() => Task.FromResult(true); + + public IReadOnlyCollection GetCommandLineOptions() + => + [ + new CommandLineOption(CrashDumpCommandLineOptions.CrashDumpOptionName, CrashDumpResources.CrashDumpOptionDescription, ArgumentArity.Zero, false), + new CommandLineOption(CrashDumpCommandLineOptions.CrashDumpFileNameOptionName, CrashDumpResources.CrashDumpFileNameOptionDescription, ArgumentArity.ExactlyOne, false), + new CommandLineOption(CrashDumpCommandLineOptions.CrashDumpTypeOptionName, CrashDumpResources.CrashDumpTypeOptionDescription, ArgumentArity.ExactlyOne, false) + ]; + + public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) + { + if (commandOption.Name == CrashDumpCommandLineOptions.CrashDumpTypeOptionName) + { + if (!DumpTypeOptions.Contains(arguments[0], StringComparer.OrdinalIgnoreCase)) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CrashDumpTypeOptionInvalidType, arguments[0])); + } + } + + return ValidationResult.ValidTask; + } + + public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) + => ValidationResult.ValidTask; +} diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpConfiguration.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpConfiguration.cs new file mode 100644 index 0000000000..b2d64c75d5 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpConfiguration.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Extensions.Diagnostics; + +internal sealed class CrashDumpConfiguration +{ + public string? DumpFileNamePattern { get; set; } + + public bool Enable { get; set; } = true; +} diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpEnvironmentVariableProvider.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpEnvironmentVariableProvider.cs new file mode 100644 index 0000000000..3826dacf6b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpEnvironmentVariableProvider.cs @@ -0,0 +1,214 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Text; + +using Microsoft.Testing.Extensions.Diagnostics.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.TestHostControllers; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Extensions.Diagnostics; + +internal sealed class CrashDumpEnvironmentVariableProvider : ITestHostEnvironmentVariableProvider +{ + private const string EnableMiniDumpVariable = "DbgEnableMiniDump"; + private const string MiniDumpTypeVariable = "DbgMiniDumpType"; + private const string MiniDumpNameVariable = "DbgMiniDumpName"; + private const string CreateDumpDiagnosticsVariable = "CreateDumpDiagnostics"; + private const string CreateDumpVerboseDiagnosticsVariable = "CreateDumpVerboseDiagnostics"; + private const string EnableMiniDumpValue = "1"; + + private readonly string[] _prefixes = ["DOTNET_", "COMPlus_"]; + private readonly IConfiguration _configuration; + private readonly IMessageBus _messageBus; + private readonly ICommandLineOptions _commandLineOptions; + private readonly ITestApplicationModuleInfo _testApplicationModuleInfo; + private readonly CrashDumpConfiguration _crashDumpGeneratorConfiguration; + private readonly ILogger _logger; + + private string? _miniDumpNameValue; + + public CrashDumpEnvironmentVariableProvider( + IConfiguration configuration, + IMessageBus messageBus, + ICommandLineOptions commandLineOptions, + ITestApplicationModuleInfo testApplicationModuleInfo, + CrashDumpConfiguration crashDumpGeneratorConfiguration, + ILoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + _configuration = configuration; + _messageBus = messageBus; + _commandLineOptions = commandLineOptions; + _testApplicationModuleInfo = testApplicationModuleInfo; + _crashDumpGeneratorConfiguration = crashDumpGeneratorConfiguration; + } + + /// + public string Uid => nameof(CrashDumpEnvironmentVariableProvider); + + /// + public string Version => AppVersion.DefaultSemVer; + + /// + public string DisplayName => CrashDumpResources.CrashDumpDisplayName; + + /// + public string Description => CrashDumpResources.CrashDumpDescription; + + /// + public Task IsEnabledAsync() + => Task.FromResult(_commandLineOptions.IsOptionSet(CrashDumpCommandLineOptions.CrashDumpOptionName) && _crashDumpGeneratorConfiguration.Enable); + + public Task UpdateAsync(IEnvironmentVariables environmentVariables) + { + foreach (string prefix in _prefixes) + { + environmentVariables.SetVariable(new($"{prefix}{EnableMiniDumpVariable}", EnableMiniDumpValue, false, true)); + environmentVariables.SetVariable(new($"{prefix}{CreateDumpDiagnosticsVariable}", EnableMiniDumpValue, false, true)); + environmentVariables.SetVariable(new($"{prefix}{CreateDumpVerboseDiagnosticsVariable}", EnableMiniDumpValue, false, true)); + } + + string miniDumpTypeValue = "4"; + + if (_commandLineOptions.TryGetOptionArgumentList(CrashDumpCommandLineOptions.CrashDumpTypeOptionName, out string[]? dumpTypeString)) + { + switch (dumpTypeString[0].ToLowerInvariant().Trim()) + { + case "mini": + { + miniDumpTypeValue = "1"; + break; + } + + case "heap": + { + miniDumpTypeValue = "2"; + break; + } + + case "triage": + { + miniDumpTypeValue = "3"; + break; + } + + case "full": + { + miniDumpTypeValue = "4"; + break; + } + + default: + { + miniDumpTypeValue = dumpTypeString[0]; + break; + } + } + } + + foreach (string prefix in _prefixes) + { + environmentVariables.SetVariable(new($"{prefix}{MiniDumpTypeVariable}", miniDumpTypeValue, false, true)); + } + + _miniDumpNameValue = _commandLineOptions.TryGetOptionArgumentList(CrashDumpCommandLineOptions.CrashDumpFileNameOptionName, out string[]? dumpFileName) + ? Path.Combine(_configuration.GetTestResultDirectory(), dumpFileName[0]) + : Path.Combine(_configuration.GetTestResultDirectory(), $"{Path.GetFileName(_testApplicationModuleInfo.GetCurrentTestApplicationFullPath())}_%p_crash.dmp"); + _crashDumpGeneratorConfiguration.DumpFileNamePattern = _miniDumpNameValue; + foreach (string prefix in _prefixes) + { + environmentVariables.SetVariable(new($"{prefix}{MiniDumpNameVariable}", _miniDumpNameValue, false, true)); + } + + if (_logger.IsEnabled(LogLevel.Trace)) + { + _logger.LogTrace($"{MiniDumpNameVariable}: {_miniDumpNameValue}"); + _logger.LogTrace($"{MiniDumpTypeVariable}: {miniDumpTypeValue}"); + } + + return Task.CompletedTask; + } + + public Task ValidateTestHostEnvironmentVariablesAsync(IReadOnlyEnvironmentVariables environmentVariables) + { + StringBuilder errors = new(); + +#if !NETCOREAPP + return ValidationResult.InvalidTask(CrashDumpResources.CrashDumpNotSupportedInNonNetCoreErrorMessage); +#else + foreach (string prefix in _prefixes) + { + if (!environmentVariables.TryGetVariable($"{prefix}{EnableMiniDumpVariable}", out OwnedEnvironmentVariable? enableMiniDump) + || enableMiniDump.Value != EnableMiniDumpValue) + { + AddError(errors, $"{prefix}{EnableMiniDumpVariable}", EnableMiniDumpValue, enableMiniDump?.Value); + } + } + + foreach (string prefix in _prefixes) + { + if (!environmentVariables.TryGetVariable($"{prefix}{CreateDumpDiagnosticsVariable}", out OwnedEnvironmentVariable? enableMiniDump) + || enableMiniDump.Value != EnableMiniDumpValue) + { + AddError(errors, $"{prefix}{CreateDumpDiagnosticsVariable}", EnableMiniDumpValue, enableMiniDump?.Value); + } + } + + foreach (string prefix in _prefixes) + { + if (!environmentVariables.TryGetVariable($"{prefix}{CreateDumpVerboseDiagnosticsVariable}", out OwnedEnvironmentVariable? enableMiniDump) + || enableMiniDump.Value != EnableMiniDumpValue) + { + AddError(errors, $"{prefix}{CreateDumpVerboseDiagnosticsVariable}", EnableMiniDumpValue, enableMiniDump?.Value); + } + } + + foreach (string prefix in _prefixes) + { + if (!environmentVariables.TryGetVariable($"{prefix}{MiniDumpTypeVariable}", out OwnedEnvironmentVariable? miniDumpType)) + { + AddError(errors, $"{prefix}{MiniDumpTypeVariable}", "Valid values are 1, 2, 3, 4", miniDumpType?.Value); + } + else + { + if (miniDumpType is null || miniDumpType.Value is null) + { + throw new InvalidOperationException("Unexpected missing MiniDumpTypeVariable variable"); + } + + if (!miniDumpType.Value.Equals("1", StringComparison.OrdinalIgnoreCase) && + !miniDumpType.Value.Equals("2", StringComparison.OrdinalIgnoreCase) && + !miniDumpType.Value.Equals("3", StringComparison.OrdinalIgnoreCase) && + !miniDumpType.Value.Equals("4", StringComparison.OrdinalIgnoreCase)) + { + AddError(errors, $"{prefix}{MiniDumpTypeVariable}", "Valid values are 1, 2, 3, 4", miniDumpType.Value); + } + } + } + + foreach (string prefix in _prefixes) + { + if (!environmentVariables.TryGetVariable($"{prefix}{MiniDumpNameVariable}", out OwnedEnvironmentVariable? miniDumpName) + || miniDumpName.Value != _miniDumpNameValue) + { + AddError(errors, $"{prefix}{MiniDumpNameVariable}", _miniDumpNameValue, miniDumpName?.Value); + } + } + + return Task.FromResult(errors.Length > 0 ? ValidationResult.Invalid(errors.ToString()) : ValidationResult.Valid()); + + static void AddError(StringBuilder errors, string variableName, string? expectedValue, string? actualValue) + { + string actualValueString = actualValue ?? ""; + errors.AppendLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, CrashDumpResources.CrashDumpInvalidEnvironmentVariableValueErrorMessage, variableName, expectedValue, actualValueString)); + } +#endif + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpExtensions.cs new file mode 100644 index 0000000000..2a5cc2f338 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpExtensions.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.Diagnostics; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Extensions; + +public static class CrashDumpExtensions +{ + public static void AddCrashDumpProvider(this ITestApplicationBuilder builder, bool ignoreIfNotSupported = false) + { + CrashDumpConfiguration crashDumpGeneratorConfiguration = new(); + + if (ignoreIfNotSupported) + { +#if !NETCOREAPP + crashDumpGeneratorConfiguration.Enable = false; +#endif + } + + builder.TestHostControllers.AddEnvironmentVariableProvider(serviceProvider + => new CrashDumpEnvironmentVariableProvider( + serviceProvider.GetConfiguration(), + serviceProvider.GetMessageBus(), + serviceProvider.GetCommandLineOptions(), + serviceProvider.GetTestApplicationModuleInfo(), + crashDumpGeneratorConfiguration, + serviceProvider.GetLoggerFactory())); + + builder.TestHostControllers.AddProcessLifetimeHandler(serviceProvider + => new CrashDumpProcessLifetimeHandler( + serviceProvider.GetCommandLineOptions(), + serviceProvider.GetMessageBus(), + serviceProvider.GetOutputDevice(), + crashDumpGeneratorConfiguration)); + + builder.CommandLine.AddProvider(() => new CrashDumpCommandLineProvider()); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpProcessLifetimeHandler.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpProcessLifetimeHandler.cs new file mode 100644 index 0000000000..9fdcbf8e24 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpProcessLifetimeHandler.cs @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.Diagnostics.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestHostControllers; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.OutputDevice; + +namespace Microsoft.Testing.Extensions.Diagnostics; + +internal sealed class CrashDumpProcessLifetimeHandler : ITestHostProcessLifetimeHandler, IDataProducer, IOutputDeviceDataProducer +{ + private readonly ICommandLineOptions _commandLineOptions; + private readonly IMessageBus _messageBus; + private readonly IOutputDevice _outputDisplay; + private readonly CrashDumpConfiguration _netCoreCrashDumpGeneratorConfiguration; + + public CrashDumpProcessLifetimeHandler( + ICommandLineOptions commandLineOptions, + IMessageBus messageBus, + IOutputDevice outputDisplay, + CrashDumpConfiguration netCoreCrashDumpGeneratorConfiguration) + { + _commandLineOptions = commandLineOptions; + _messageBus = messageBus; + _outputDisplay = outputDisplay; + _netCoreCrashDumpGeneratorConfiguration = netCoreCrashDumpGeneratorConfiguration; + } + + /// + public string Uid => nameof(CrashDumpProcessLifetimeHandler); + + /// + public string Version => AppVersion.DefaultSemVer; + + /// + public string DisplayName => CrashDumpResources.CrashDumpDisplayName; + + /// + public string Description => CrashDumpResources.CrashDumpDescription; + + public Type[] DataTypesProduced => [typeof(FileArtifact)]; + + public Task IsEnabledAsync() + => Task.FromResult(_commandLineOptions.IsOptionSet(CrashDumpCommandLineOptions.CrashDumpOptionName) + && _netCoreCrashDumpGeneratorConfiguration.Enable); + + public Task BeforeTestHostProcessStartAsync(CancellationToken _) => Task.CompletedTask; + + public Task OnTestHostProcessStartedAsync(ITestHostProcessInformation testHostProcessInformation, CancellationToken cancellation) => Task.CompletedTask; + + public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testHostProcessInformation, CancellationToken cancellation) + { + if (cancellation.IsCancellationRequested + || testHostProcessInformation.HasExitedGracefully + || (AppDomain.CurrentDomain.GetData("ProcessKilledByHangDump") is string processKilledByHangDump && processKilledByHangDump == "true")) + { + return; + } + + ApplicationStateGuard.Ensure(_netCoreCrashDumpGeneratorConfiguration.DumpFileNamePattern is not null); + await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CrashDumpProcessCrashedDumpFileCreated, testHostProcessInformation.PID))); + + string expectedDumpFile = _netCoreCrashDumpGeneratorConfiguration.DumpFileNamePattern.Replace("%p", testHostProcessInformation.PID.ToString(CultureInfo.InvariantCulture)); + if (File.Exists(expectedDumpFile)) + { + await _messageBus.PublishAsync(this, new FileArtifact(new FileInfo(expectedDumpFile), CrashDumpResources.CrashDumpArtifactDisplayName, CrashDumpResources.CrashDumpArtifactDescription)); + } + else + { + await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CannotFindExpectedCrashDumpFile, expectedDumpFile))); + foreach (string dumpFile in Directory.GetFiles(Path.GetDirectoryName(expectedDumpFile)!, "*.dmp")) + { + await _messageBus.PublishAsync(this, new FileArtifact(new FileInfo(dumpFile), CrashDumpResources.CrashDumpDisplayName, CrashDumpResources.CrashDumpArtifactDescription)); + } + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Microsoft.Testing.Extensions.CrashDump.csproj b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Microsoft.Testing.Extensions.CrashDump.csproj new file mode 100644 index 0000000000..9ab987f07a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Microsoft.Testing.Extensions.CrashDump.csproj @@ -0,0 +1,65 @@ + + + + netstandard2.0;$(MicrosoftTestingTargetFrameworks) + Microsoft.Testing.Extensions.Diagnostics + + + + + + + + + + + + + + true + buildMultiTargeting + + + buildTransitive/$(TargetFramework) + + + build/$(TargetFramework) + + + + + + + + + + + + + + + + + + + + + + + + True + True + CrashDumpResources.resx + + + + + + ResXFileCodeGenerator + CrashDumpResources.Designer.cs + + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/PACKAGE.md b/src/Platform/Microsoft.Testing.Extensions.CrashDump/PACKAGE.md new file mode 100644 index 0000000000..88c8efbf23 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/PACKAGE.md @@ -0,0 +1,9 @@ +# Microsoft.Testing + +Microsoft Testing is a set of platform, framework and protocol intended to make it possible to run any test on any target or device. + +Documentation can be found at . + +## About + +This package extends Microsoft Testing Platform to provide a crash dump functionality. diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.CrashDump/PublicAPI/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..4dccf2ae97 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/PublicAPI/PublicAPI.Shipped.txt @@ -0,0 +1,5 @@ +#nullable enable +Microsoft.Testing.Extensions.CrashDump.TestingPlatformBuilderHook +Microsoft.Testing.Extensions.CrashDumpExtensions +static Microsoft.Testing.Extensions.CrashDump.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! _) -> void +static Microsoft.Testing.Extensions.CrashDumpExtensions.AddCrashDumpProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder, bool ignoreIfNotSupported = false) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.CrashDump/PublicAPI/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/CrashDumpResources.Designer.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/CrashDumpResources.Designer.cs new file mode 100644 index 0000000000..c316012e7d --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/CrashDumpResources.Designer.cs @@ -0,0 +1,189 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Testing.Extensions.Diagnostics.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class CrashDumpResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CrashDumpResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Testing.Extensions.Diagnostics.Resources.CrashDumpResources", typeof(CrashDumpResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder. + /// + internal static string CannotFindExpectedCrashDumpFile { + get { + return ResourceManager.GetString("CannotFindExpectedCrashDumpFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The testhost process crash dump file. + /// + internal static string CrashDumpArtifactDescription { + get { + return ResourceManager.GetString("CrashDumpArtifactDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Crash dump file. + /// + internal static string CrashDumpArtifactDisplayName { + get { + return ResourceManager.GetString("CrashDumpArtifactDisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly. + /// + internal static string CrashDumpDescription { + get { + return ResourceManager.GetString("CrashDumpDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Crash dump. + /// + internal static string CrashDumpDisplayName { + get { + return ResourceManager.GetString("CrashDumpDisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specify the name of the dump file. + /// + internal static string CrashDumpFileNameOptionDescription { + get { + return ResourceManager.GetString("CrashDumpFileNameOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Environment variable '{0}' should have been set to '{1}' but value is '{2}'. + /// + internal static string CrashDumpInvalidEnvironmentVariableValueErrorMessage { + get { + return ResourceManager.GetString("CrashDumpInvalidEnvironmentVariableValueErrorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Crash dump feature is only available in .NET (Core). + /// + internal static string CrashDumpNotSupportedInNonNetCoreErrorMessage { + get { + return ResourceManager.GetString("CrashDumpNotSupportedInNonNetCoreErrorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [net6.0+ only] Generate a dump file if the test process crashes. + /// + internal static string CrashDumpOptionDescription { + get { + return ResourceManager.GetString("CrashDumpOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Test host process with PID '{0}' crashed, a dump file was generated. + /// + internal static string CrashDumpProcessCrashedDumpFileCreated { + get { + return ResourceManager.GetString("CrashDumpProcessCrashedDumpFileCreated", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps. + /// + internal static string CrashDumpTypeOptionDescription { + get { + return ResourceManager.GetString("CrashDumpTypeOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full'. + /// + internal static string CrashDumpTypeOptionInvalidType { + get { + return ResourceManager.GetString("CrashDumpTypeOptionInvalidType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap'). + /// + internal static string CrashDumpTypeOptionTooManyArguments { + get { + return ResourceManager.GetString("CrashDumpTypeOptionTooManyArguments", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Requests of type '{0}' is not supported. + /// + internal static string UnsupportedRequestTypeErrorMessage { + get { + return ResourceManager.GetString("UnsupportedRequestTypeErrorMessage", resourceCulture); + } + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/CrashDumpResources.resx b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/CrashDumpResources.resx new file mode 100644 index 0000000000..c97e098e10 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/CrashDumpResources.resx @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + + + The testhost process crash dump file + + + Crash dump file + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + + + Crash dump + + + Specify the name of the dump file + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + + + Crash dump feature is only available in .NET (Core) + + + [net6.0+ only] Generate a dump file if the test process crashes + + + Test host process with PID '{0}' crashed, a dump file was generated + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + + + Requests of type '{0}' is not supported + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.cs.xlf new file mode 100644 index 0000000000..785df2fe2e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.cs.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + Nebyl nalezen očekávaný soubor výpisu stavu systému {0}. Všechny soubory odpovídající vzoru *.dmp budou zkopírovány do složky výsledků. + + + + The testhost process crash dump file + Soubor výpisu stavu systému procesu testhost + + + + Crash dump file + Soubor výpisu stavu systému + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [Pouze net6.0+ ] Vytvořit soubory výpisu stavu systému při neočekávaném chybovém ukončení procesu provádění testu + + + + Crash dump + Výpis stavu systému + + + + Specify the name of the dump file + Zadejte název souboru výpisu paměti. + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + Proměnná prostředí {0} by měla být nastavená na {1}, ale hodnota je {2}. + + + + Crash dump feature is only available in .NET (Core) + Funkce výpisu stavu systému je k dispozici pouze v rozhraní .NET (Core). + + + + [net6.0+ only] Generate a dump file if the test process crashes + [Pouze net6.0+ ] Vygenerovat soubor výpisu paměti v případě chybového ukončení procesu testu + + + + Test host process with PID '{0}' crashed, a dump file was generated + Hostitelský proces testu s identifikátorem PID {0} byl chybově ukončen. Byl vygenerován soubor výpisu paměti. + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + Zadejte typ výpisu paměti. Platné hodnoty jsou Mini, Heap, Triage nebo Full. Výchozí typ je Full. Další informace najdete na https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + {0} není platný typ výpisu paměti. Platné možnosti jsou Mini, Heap, Triage a Full. + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + --hangdump-type očekává jako argument jeden typ výpisu paměti (například --hangdump-type Heap). + + + + Requests of type '{0}' is not supported + Žádosti typu {0} nejsou podporovány. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.de.xlf new file mode 100644 index 0000000000..acd0d66c37 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.de.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + Die erwartete Absturzabbilddatei "{0}" wurde nicht gefunden. Alle Dateien, die dem Muster "*.dmp" entsprechen, werden in den Ergebnisordner kopiert + + + + The testhost process crash dump file + Die Absturzabbilddatei für den Testhostprozess + + + + Crash dump file + Absturzabbilddatei + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [nur net6.0+] Erstellen von Absturzabbilddateien, wenn der Testausführungsprozess unerwartet abstürzt + + + + Crash dump + Absturzspeicherabbild + + + + Specify the name of the dump file + Namen der Speicherabbilddatei angeben + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + Die Umgebungsvariable "{0}" hätte auf "{1}" festgelegt werden sollen, aber der Wert ist "{2}" + + + + Crash dump feature is only available in .NET (Core) + Das Absturzabbildfeature ist nur in .NET (Core) verfügbar + + + + [net6.0+ only] Generate a dump file if the test process crashes + [nur net6.0+] Speicherabbilddatei generieren, wenn der Testprozess abstürzt + + + + Test host process with PID '{0}' crashed, a dump file was generated + Der Testhostprozess mit PID "{0}" ist abgestürzt. Es wurde eine Abbilddatei generiert + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + Geben Sie den Typ des Speicherabbilds an. Gültige Werte sind „Mini“, „Heap“, „Triage“ oder „Full“. Der Standardtyp ist „Full“. Weitere Informationen finden Sie unter https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + "{0}" ist kein gültiger Speicherabbildtyp. Gültige Optionen sind "Mini", "Heap", "Triage" und "Full" + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + "--crashdump-type" erwartet einen einzelnen Speicherabbildtyp als Argument (z. B. "--crashdump-type Heap") + + + + Requests of type '{0}' is not supported + Anforderungen vom Typ "{0}" werden nicht unterstützt + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.es.xlf new file mode 100644 index 0000000000..df69ed55fb --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.es.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + No se encontró el '{0}' de archivo de volcado esperado. Todos los archivos que coincidan con el patrón "*.dmp" se copiarán en la carpeta de resultados. + + + + The testhost process crash dump file + Archivo de volcado de memoria del proceso del host de pruebas + + + + Crash dump file + Archivo de volcado de memoria + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [solo net6.0+ ] Generar archivos de volcado de memoria cuando el proceso de ejecución de pruebas se bloquea inesperadamente + + + + Crash dump + Volcado de memoria + + + + Specify the name of the dump file + Especificar el nombre del archivo de volcado + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + La variable de entorno '{0}' debe haberse establecido en '{1}' pero el valor es '{2}' + + + + Crash dump feature is only available in .NET (Core) + La característica de volcado de memoria solo está disponible en .NET (Core) + + + + [net6.0+ only] Generate a dump file if the test process crashes + [solo net6.0+ ] Generar un archivo de volcado si el proceso de prueba se bloquea + + + + Test host process with PID '{0}' crashed, a dump file was generated + Se bloqueó el proceso de host de prueba con PID '{0}' y se generó un archivo de volcado + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + Especifique el tipo de volcado. Los valores válidos son "Mini", "Heap", "Evaluación de prioridades" o "Completo". El tipo predeterminado es 'Completo'. Para obtener más información, visite https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + '{0}' no es un tipo de volcado válido. Las opciones válidas son "Mini", "Montón", "Evaluación de prioridades" y "Completa" + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + '--crashdump-type' espera un único tipo de volcado como argumento (por ejemplo, '--crashdump-type Heap') + + + + Requests of type '{0}' is not supported + No se admiten solicitudes de tipo '{0}' + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.fr.xlf new file mode 100644 index 0000000000..11cd3acf39 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.fr.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + Le fichier de vidage sur incident attendu « {0} » n'a pas été trouvé, tous les fichiers correspondant au modèle « *.dmp » seront copiés dans le dossier de résultats. + + + + The testhost process crash dump file + Le fichier de vidage sur incident du processus testhost + + + + Crash dump file + Fichier de vidage sur incident + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [net6.0+ uniquement] Produire des fichiers de vidage sur incident lorsque le processus d’exécution des tests se bloque de manière inattendue + + + + Crash dump + Vidage sur incident + + + + Specify the name of the dump file + Spécifier le nom du fichier de vidage + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + La variable d’environnement «{0}» doit avoir la valeur «{1}», mais la valeur est «{2}» + + + + Crash dump feature is only available in .NET (Core) + La fonctionnalité de vidage sur incident est disponible uniquement dans .NET (Core) + + + + [net6.0+ only] Generate a dump file if the test process crashes + [net6.0+ uniquement] Générer un fichier de vidage si le processus de test se bloque + + + + Test host process with PID '{0}' crashed, a dump file was generated + Le processus hôte de test avec le PID «{0}» s’est arrêté, un fichier de vidage a été généré + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + Spécifiez le type de l’image mémoire. Les valeurs valides sont « Mini », « Heap », « Triage » ou « Full ». Le type par défaut est « Full ». Pour plus d’informations, visitez https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + '{0}' n’est pas un type de vidage valide. Les options valides sont « Mini », « Heap », « Triage » et « Full » + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + '--crashdump-type' attend un seul type de dump comme argument (par exemple '--crashdump-type Heap') + + + + Requests of type '{0}' is not supported + Les demandes de type «{0}» ne sont pas prises en charge + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.it.xlf new file mode 100644 index 0000000000..c0c7a2cf77 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.it.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + Il file di dump di arresto anomalo '{0}' previsto non è stato trovato. Tutti i file corrispondenti al criterio '*.dmp' verranno copiati nella cartella dei risultati. + + + + The testhost process crash dump file + File di dump di arresto anomalo del processo testhost + + + + Crash dump file + File di dump di arresto anomalo del sistema + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [solo net6.0+] Generazione di file di dump di arresto anomalo quando il processo di esecuzione del test si arresta in modo imprevisto + + + + Crash dump + Dump di arresto anomalo del sistema + + + + Specify the name of the dump file + Specificare il nome del file di dump + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + La variabile di ambiente '{0}' avrebbe dovuto essere impostata su '{1}' ma il valore è '{2}' + + + + Crash dump feature is only available in .NET (Core) + La funzionalità dump di arresto anomalo del sistema è disponibile solo in .NET (Core) + + + + [net6.0+ only] Generate a dump file if the test process crashes + [solo net6.0+ ] Generazione di un file di dump in caso di arresto anomalo del processo di test + + + + Test host process with PID '{0}' crashed, a dump file was generated + Il processo host di test con PID '{0}' si è arrestato in modo anomalo. È stato generato un file di dump + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + Specificare il tipo di dump. I valori validi sono "Mini", "Heap", "Valutazione" o "Completo". Il tipo predefinito è "Completo". Per altre informazioni, visitare https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + '{0}' non è un tipo di dump valido. Le opzioni valide sono 'Mini', 'Heap', 'Triage' e 'Full' + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + '--crashdump-type' prevede un singolo tipo di dump come argomento (ad esempio '--crashdump-type Heap') + + + + Requests of type '{0}' is not supported + Le richieste di tipo '{0}' non sono supportate + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.ja.xlf new file mode 100644 index 0000000000..b0a6687ff4 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.ja.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + 予期されたクラッシュ ダンプ ファイル '{0}' が見つかりませんでした。'*.dmp' パターンに一致するすべてのファイルが結果フォルダーにコピーされます + + + + The testhost process crash dump file + testhost プロセスのクラッシュ ダンプ ファイル + + + + Crash dump file + クラッシュ ダンプ ファイル + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [net6.0+ のみ] テストの実行プロセスが予期せずクラッシュしたときにクラッシュ ダンプ ファイルを生成する + + + + Crash dump + クラッシュ ダンプ + + + + Specify the name of the dump file + ダンプ ファイルの名前を指定する + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + 環境変数 '{0}' は '{1}' に設定する必要がありますが、値は '{2}' です + + + + Crash dump feature is only available in .NET (Core) + クラッシュ ダンプ機能は .NET (Core) でのみ使用できます + + + + [net6.0+ only] Generate a dump file if the test process crashes + [net6.0+ のみ] テスト プロセスがクラッシュした場合にダンプ ファイルを生成する + + + + Test host process with PID '{0}' crashed, a dump file was generated + PID '{0}' のテスト ホスト プロセスがクラッシュしました。ダンプ ファイルが生成されました + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + ダンプの型を指定します。有効な値は、'Mini'、'Heap'、'Triage'、または 'Full' です。既定の型は 'Full' です。詳細については、https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps を参照してください + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + '{0}' は有効なダンプの種類ではありません。有効なオプションは、'Mini'、'Heap'、'Triage'、'Full' です + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + '--crashdump-type' には、引数として 1 つのダンプの型が必要です (例: '--crashdump-type Heap') + + + + Requests of type '{0}' is not supported + 型 '{0}' の要求はサポートされていません + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.ko.xlf new file mode 100644 index 0000000000..3f226f41bf --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.ko.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + 필요한 크래시 덤프 파일 '{0}'을(를) 찾을 수 없습니다. '*.dmp' 패턴과 일치하는 모든 파일이 결과 폴더에 복사됩니다. + + + + The testhost process crash dump file + testhost 프로세스 크래시 덤프 파일 + + + + Crash dump file + 크래시 덤프 파일 + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [net6.0 이상만] 테스트 실행 프로세스가 예기치 않게 충돌할 때 크래시 덤프 파일 생성 + + + + Crash dump + 크래시 덤프 + + + + Specify the name of the dump file + 덤프 파일의 이름 지정 + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + 환경 변수 '{0}'은(는) '{1}'로 설정해야 하지만 값이 '{2}'입니다. + + + + Crash dump feature is only available in .NET (Core) + 크래시 덤프 기능은 .NET(Core)에서만 사용할 수 있습니다. + + + + [net6.0+ only] Generate a dump file if the test process crashes + [net6.0 이상만] 테스트 프로세스가 충돌하는 경우 덤프 파일 생성 + + + + Test host process with PID '{0}' crashed, a dump file was generated + PID가 '{0}'인 테스트 호스트 프로세스가 충돌했습니다. 덤프 파일이 생성되었습니다. + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + 덤프 유형을 지정합니다. 유효한 값은 'Mini', 'Heap', 'Triage' 또는 'Full'입니다. 기본 유형은 'Full'입니다. 자세한 내용은 https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps를 방문하세요. + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + '{0}'은(는) 올바른 덤프 유형이 아닙니다. 유효한 옵션은 'Mini', 'Heap', 'Triage' 및 'Full'입니다. + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + '--crashdump-type'에는 단일 덤프 유형이 인수로 필요합니다(예: '--crashdump-type Heap'). + + + + Requests of type '{0}' is not supported + '{0}' 유형의 요청은 지원되지 않습니다. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.pl.xlf new file mode 100644 index 0000000000..09a62f5adb --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.pl.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + Nie można odnaleźć oczekiwanego pliku zrzutu awaryjnego „{0}”. Wszystkie pliki zgodne ze wzorcem „*.dmp” zostaną skopiowane do folderu wyników + + + + The testhost process crash dump file + Plik zrzutu awaryjnego procesu testhost + + + + Crash dump file + Plik zrzutu awaryjnego + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [tylko net6.0+ ] Tworzenie plików zrzutu awaryjnego w przypadku nieoczekiwanej awarii procesu wykonywania testu + + + + Crash dump + Zrzut awaryjny + + + + Specify the name of the dump file + Określ nazwę pliku zrzutu + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + Zmienna środowiskowa „{0}” powinna być ustawiona na „{1}”, ale wartość to „{2}” + + + + Crash dump feature is only available in .NET (Core) + Funkcja zrzutu awaryjnego jest dostępna tylko na platformie .NET (Core) + + + + [net6.0+ only] Generate a dump file if the test process crashes + [tylko net6.0+ ] Wygeneruj plik zrzutu w przypadku awarii procesu testowego + + + + Test host process with PID '{0}' crashed, a dump file was generated + Proces hosta testowego o identyfikatorze PID „{0}{0}” uległ awarii, wygenerowano plik zrzutu + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + Określ typ zrzutu. Prawidłowe wartości to „Mini”, „Sterta”, „Klasyfikacja” i „Pełne”. Typ domyślny to „Pełne”. Aby uzyskać więcej informacji, odwiedź stronę https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + Typ „{0}” nie jest prawidłowym typem zrzutu. Prawidłowe opcje to „Mini”, „Sterta”, „Klasyfikacja” i „Pełne” + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + Element „--crashdump-type” oczekuje pojedynczego typu zrzutu jako argumentu (np. „--crashdump-type Heap”) + + + + Requests of type '{0}' is not supported + Żądania typu „{0}” nie są obsługiwane + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.pt-BR.xlf new file mode 100644 index 0000000000..1361a936e1 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.pt-BR.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + O arquivo de despejo de memória ''{0}'' não pôde ser encontrado. Todos os arquivos correspondentes ao padrão ''*.dmp'' serão copiados para a pasta de resultados + + + + The testhost process crash dump file + O arquivo de despejo de memória do processo testhost + + + + Crash dump file + Arquivo de despejo de memória + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [somente net6.0+] Produzir arquivos de despejo de memória quando o processo de execução de teste falhar inesperadamente + + + + Crash dump + Despejo de memória + + + + Specify the name of the dump file + Especifique o nome do arquivo de despejo + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + A variável de ambiente ''{0}'' deveria ter sido definida como ''{1}'', mas o valor é ''{2}'' + + + + Crash dump feature is only available in .NET (Core) + O recurso de despejo de memória só está disponível no .NET (Core) + + + + [net6.0+ only] Generate a dump file if the test process crashes + [somente net6.0+] Gerar um arquivo de despejo se o processo de teste falhar + + + + Test host process with PID '{0}' crashed, a dump file was generated + O processo de host de teste com PID ''{0}'' falhou e um arquivo de despejo foi gerado + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + Especifique o tipo de despejo. Os valores válidos são ''Mini'', ''Heap'', ''Triage'' ou ''Full''. O tipo padrão é ''Full''. Para obter mais informações, visite https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + ''{0}'' não é um tipo de despejo válido. As opções válidas são ''Mini'', ''Heap'', ''Triage'' e ''Full'' + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + ''--crashdump-type'' espera um único tipo de despejo como argumento (por exemplo, ''--crashdump-type Heap'') + + + + Requests of type '{0}' is not supported + Não há suporte para solicitações de tipo ''{0}'' + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.ru.xlf new file mode 100644 index 0000000000..c1c1c92d71 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.ru.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + Не удалось найти ожидаемый файл аварийного дампа "{0}". Все файлы, соответствующие шаблону "*.dmp", будут скопированы в папку результатов. + + + + The testhost process crash dump file + Файл аварийного дампа тестового хост-процесса + + + + Crash dump file + Файл аварийного дампа + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [только net6.0 и выше] Создавать файлы аварийного дампа при неожиданном сбое процесса выполнения теста + + + + Crash dump + Аварийный дамп + + + + Specify the name of the dump file + Укажите имя файла дампа зависания + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + Для переменной среды "{0}" должно быть задано значение "{1}", но задано значение "{2}" + + + + Crash dump feature is only available in .NET (Core) + Функция аварийного дампа доступна только в .NET (Core) + + + + [net6.0+ only] Generate a dump file if the test process crashes + [только net6.0 и выше] Создавать файл дампа в случае сбоя тестового процесса + + + + Test host process with PID '{0}' crashed, a dump file was generated + Произошло аварийное завершение тестового хост-процесса с идентификатором процесса "{0}". Создан файл дампа + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + Укажите тип дампа. Допустимые значения: "Mini", "Heap", "Triage" или "Full". Тип по умолчанию — "Full". Дополнительные сведения см. на странице https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + "{0}" не является допустимым типом дампа. Допустимые параметры: "Mini", "Heap", "Triage" и "Full" + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + "--hangdump-type" ожидает в качестве аргумента один тип дампа (например, "--crashdump-type Heap") + + + + Requests of type '{0}' is not supported + Запросы типа "{0}" не поддерживаются + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.tr.xlf new file mode 100644 index 0000000000..c1e302d08c --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.tr.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + Beklenen kilitlenme dökümü dosyası '{0}' bulunamadı, '*.dmp' düzeniyle eşleşen tüm dosyalar sonuç klasörüne kopyalanacak + + + + The testhost process crash dump file + Test ana bilgisayarı işlemi kilitlenme bilgi dökümü dosyası + + + + Crash dump file + Kilitlenme bilgi dökümü dosyası + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [yalnızca net6.0+] Test yürütme işlemi beklenmedik bir şekilde çöktüğünde kilitlenme dökümü dosyaları oluştur + + + + Crash dump + Kilitlenme bilgi dökümü + + + + Specify the name of the dump file + Döküm dosyasının adını belirtin + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + Ortam değişkeni '{0}', '{1}' olarak ayarlanmalıydı ancak değer '{2}' + + + + Crash dump feature is only available in .NET (Core) + Kilitlenme dökümü özelliği yalnızca .NET'te (Core) mevcuttur + + + + [net6.0+ only] Generate a dump file if the test process crashes + [yalnızca net6.0+] Test işlemi çökerse bir döküm dosyası oluşturun + + + + Test host process with PID '{0}' crashed, a dump file was generated + PID'li test ana işlemi ‘{0}' kilitlendi, bir döküm dosyası oluşturuldu + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + Dökümün türünü belirtin. Geçerli değerler: 'Mini', 'Heap', 'Triage' veya 'Full'. Varsayılan tür: 'Full'. Daha fazla bilgi için https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps adresini ziyaret edin + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + '{0}' geçerli bir döküm türü değil. Geçerli seçenekler 'Mini', 'Heap', 'Triage' ve 'Full’dur + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + '--crashdump-type' argüman olarak tek bir döküm tipini bekler (örneğin, '--crashdump-type Heap') + + + + Requests of type '{0}' is not supported + '{0}' türündeki istek desteklenmiyor + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.zh-Hans.xlf new file mode 100644 index 0000000000..230f96271b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.zh-Hans.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + 找不到预期的故障转储文件“{0}”,与“*.dmp”模式匹配的所有文件都将复制到结果文件夹 + + + + The testhost process crash dump file + testhost 进程故障转储文件 + + + + Crash dump file + 故障转储文件 + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [仅限 net6.0+]在测试执行进程意外崩溃时生成故障转储文件 + + + + Crash dump + 故障转储 + + + + Specify the name of the dump file + 指定转储文件的名称 + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + 环境变量“{0}”应设置为“{1}”,但值为“{2}” + + + + Crash dump feature is only available in .NET (Core) + 故障转储功能仅在 .NET (Core) 中可用 + + + + [net6.0+ only] Generate a dump file if the test process crashes + [仅限 net6.0+]如果测试进程崩溃,则生成转储文件 + + + + Test host process with PID '{0}' crashed, a dump file was generated + PID 为“{0}”的测试主机进程崩溃,生成了转储文件 + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + 指定转储的类型。有效值为 "Mini"、"Heap"、"Triage" 或 "Full"。默认类型为 "Full"。有关详细信息,请访问 https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + “{0}”不是有效的转储类型。有效选项为“Mini”、“Heap”、“Triage”和“Full” + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + “--crashdump-type”需要将单一转储类型作为参数(例如“--crashdump-type Heap”) + + + + Requests of type '{0}' is not supported + 不支持类型为“{0}”的请求 + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.zh-Hant.xlf new file mode 100644 index 0000000000..63cf43aed5 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Resources/xlf/CrashDumpResources.zh-Hant.xlf @@ -0,0 +1,77 @@ + + + + + + Expected crash dump file '{0}' could not be found, all files matching the '*.dmp' pattern will be copied to the result folder + 找不到預期的損毀傾印檔案 '{0}',符合 '*.dmp' 模式的所有檔案都將複製到結果資料夾 + + + + The testhost process crash dump file + testhost 處理常式損毀傾印檔案 + + + + Crash dump file + 損毀傾印檔案 + + + + [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + [net6.0+ only] 測試執行流程意外損毀時會產生損毀傾印檔案 + + + + Crash dump + 損毀傾印 + + + + Specify the name of the dump file + 指定傾印檔案的名稱 + + + + Environment variable '{0}' should have been set to '{1}' but value is '{2}' + 環境變數 '{0}' 應該設定為 '{1}' 但值為 '{2}' + + + + Crash dump feature is only available in .NET (Core) + 只有 .NET (Core) 才可使用損毀傾印功能 + + + + [net6.0+ only] Generate a dump file if the test process crashes + [net6.0+ only] 如果測試流程損毀,則產生傾印檔案 + + + + Test host process with PID '{0}' crashed, a dump file was generated + PID '{0}' 損毀的測試主機處理常式,已產生傾印檔案 + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + 指定傾印的類型。有效值為 'Mini'、'Heap'、'Triage' 或 'Full'。預設類型為 'Full'。如需詳細資訊,請瀏覽 https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full' + '{0}' 不是有效的傾印類型。有效的選項為 'Mini'、'Heap'、'Triage' 和 'Full' + + + + '--crashdump-type' expects a single dump type as argument (e.g. '--crashdump-type Heap') + '--crashdump-type' 需要單一傾印類型做為引數 (例如 '--crashdump-type Heap') + + + + Requests of type '{0}' is not supported + 不支援類型 '{0}' 的要求 + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/TestingPlatformBuilderHook.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/TestingPlatformBuilderHook.cs new file mode 100644 index 0000000000..dc583349ff --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/TestingPlatformBuilderHook.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Builder; + +namespace Microsoft.Testing.Extensions.CrashDump; + +public static class TestingPlatformBuilderHook +{ + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _) + => testApplicationBuilder.AddCrashDumpProvider(ignoreIfNotSupported: true); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/build/Microsoft.Testing.Extensions.CrashDump.props b/src/Platform/Microsoft.Testing.Extensions.CrashDump/build/Microsoft.Testing.Extensions.CrashDump.props new file mode 100644 index 0000000000..80d9f6fe11 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/build/Microsoft.Testing.Extensions.CrashDump.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/buildMultiTargeting/Microsoft.Testing.Extensions.CrashDump.props b/src/Platform/Microsoft.Testing.Extensions.CrashDump/buildMultiTargeting/Microsoft.Testing.Extensions.CrashDump.props new file mode 100644 index 0000000000..d94b2a763e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/buildMultiTargeting/Microsoft.Testing.Extensions.CrashDump.props @@ -0,0 +1,14 @@ + + + + + + Microsoft.Testing.Extensions.CrashDump + Microsoft.Testing.Extensions.CrashDump.TestingPlatformBuilderHook + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/buildTransitive/Microsoft.Testing.Extensions.CrashDump.props b/src/Platform/Microsoft.Testing.Extensions.CrashDump/buildTransitive/Microsoft.Testing.Extensions.CrashDump.props new file mode 100644 index 0000000000..80d9f6fe11 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/buildTransitive/Microsoft.Testing.Extensions.CrashDump.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.HangDump/BannedSymbols.txt new file mode 100644 index 0000000000..ea8617fcb0 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/BannedSymbols.txt @@ -0,0 +1,10 @@ +T:System.ArgumentNullException; Use 'ArgumentGuard' instead +P:System.DateTime.Now; Use 'IClock' instead +P:System.DateTime.UtcNow; Use 'IClock' instead +M:System.Threading.Tasks.Task.Run(System.Action); Use 'ITask' instead +M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask' instead +M:System.Threading.Tasks.Task.WhenAll(System.Collections.Generic.IEnumerable{System.Threading.Tasks.Task}); Use 'ITask' instead +M:System.String.IsNullOrEmpty(System.String); Use 'RoslynString.IsNullOrEmpty' instead +M:System.String.IsNullOrWhiteSpace(System.String); Use 'RoslynString.IsNullOrWhiteSpace' instead +M:System.Diagnostics.Debug.Assert(System.Boolean); Use 'RoslynDebug.Assert' instead +M:System.Diagnostics.Debug.Assert(System.Boolean,System.String); Use 'RoslynDebug.Assert' instead diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs new file mode 100644 index 0000000000..bde5b6043b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs @@ -0,0 +1,305 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Concurrent; +using System.Globalization; + +using Microsoft.Testing.Extensions.Diagnostics.Resources; +using Microsoft.Testing.Extensions.HangDump.Serializers; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Models; +using Microsoft.Testing.Platform.IPC.Serializers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Services; +using Microsoft.Testing.Platform.TestHost; + +namespace Microsoft.Testing.Extensions.Diagnostics; + +internal sealed class HangDumpActivityIndicator : IDataConsumer, ITestSessionLifetimeHandler, +#if NETCOREAPP + IAsyncDisposable +#else + IDisposable +#endif +{ + private readonly ICommandLineOptions _commandLineOptions; + private readonly IEnvironment _environment; + private readonly ITask _task; + private readonly IClock _clock; + private readonly ILogger _logger; + private readonly NamedPipeClient? _namedPipeClient; + private readonly ManualResetEventSlim _signalActivity = new(false); + private readonly ManualResetEventSlim _mutexCreated = new(false); + private readonly bool _traceLevelEnabled; + private readonly ConcurrentDictionary _testsCurrentExecutionState = new(); + + private Task? _signalActivityIndicatorTask; + private Mutex? _activityIndicatorMutex; + private string? _mutexName; + private bool _exitSignalActivityIndicatorAsync; + private NamedPipeServer? _singleConnectionNamedPipeServer; + private PipeNameDescription? _pipeNameDescription; + private bool _sessionEndCalled; + + public HangDumpActivityIndicator( + ICommandLineOptions commandLineOptions, + IEnvironment environment, + ITask task, + ITestApplicationModuleInfo testApplicationModuleInfo, + ILoggerFactory loggerFactory, + IClock clock) + { + _logger = loggerFactory.CreateLogger(); + _traceLevelEnabled = _logger.IsEnabled(LogLevel.Trace); + _commandLineOptions = commandLineOptions; + _environment = environment; + _task = task; + _clock = clock; + if (_commandLineOptions.IsOptionSet(HangDumpCommandLineProvider.HangDumpOptionName) && + !_commandLineOptions.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey)) + { + string namedPipeSuffix = _environment.GetEnvironmentVariable(HangDumpConfiguration.MutexNameSuffix) + ?? throw new InvalidOperationException($"Expected {HangDumpConfiguration.MutexNameSuffix} environment variable set."); + // @Marco: Why do we need to duplicate logic here instead of using HangDumpConfiguration.PipeNameKey? + string pipeNameEnvironmentVariable = $"{HangDumpConfiguration.PipeName}_{FNV_1aHashHelper.ComputeStringHash(testApplicationModuleInfo.GetCurrentTestApplicationFullPath())}_{namedPipeSuffix}"; + string namedPipeName = _environment.GetEnvironmentVariable(pipeNameEnvironmentVariable) + ?? throw new InvalidOperationException($"Expected {pipeNameEnvironmentVariable} environment variable set."); + _namedPipeClient = new NamedPipeClient(namedPipeName); + _namedPipeClient.RegisterSerializer(new ActivityIndicatorMutexNameRequestSerializer(), typeof(ActivityIndicatorMutexNameRequest)); + _namedPipeClient.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + _namedPipeClient.RegisterSerializer(new SessionEndSerializerRequestSerializer(), typeof(SessionEndSerializerRequest)); + _namedPipeClient.RegisterSerializer(new ConsumerPipeNameRequestSerializer(), typeof(ConsumerPipeNameRequest)); + } + } + + public Type[] DataTypesConsumed => [typeof(TestNodeUpdateMessage)]; + + public string Uid => nameof(HangDumpActivityIndicator); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => ExtensionResources.HangDumpExtensionDisplayName; + + public string Description => ExtensionResources.HangDumpExtensionDescription; + + public Task IsEnabledAsync() => Task.FromResult(_commandLineOptions.IsOptionSet(HangDumpCommandLineProvider.HangDumpOptionName) && + !_commandLineOptions.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey)); + + public async Task OnTestSessionStartingAsync(SessionUid sessionUid, CancellationToken cancellationToken) + { + ApplicationStateGuard.Ensure(_namedPipeClient is not null); + + if (!await IsEnabledAsync() || cancellationToken.IsCancellationRequested) + { + return; + } + + try + { + // Connect to the named pipe server + await _logger.LogTraceAsync($"Connecting to the process lifetime handler {_namedPipeClient.PipeName}"); + await _namedPipeClient.ConnectAsync(cancellationToken).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + await _logger.LogTraceAsync("Connected to the process lifetime handler"); + _activityIndicatorMutex = new Mutex(true); + _mutexName = $"{HangDumpConfiguration.MutexName}_{Guid.NewGuid():N}"; + + // Keep the custom thread to avoid to waste one from thread pool. + _signalActivityIndicatorTask = _task.RunLongRunning(SignalActivityIndicatorAsync, "[HangDump] SignalActivityIndicatorAsync", cancellationToken); + await _logger.LogTraceAsync($"Wait for mutex '{_mutexName}' creation"); + _mutexCreated.Wait(cancellationToken); + await _logger.LogTraceAsync($"Mutex '{_mutexName}' created"); + await _namedPipeClient.RequestReplyAsync(new ActivityIndicatorMutexNameRequest(_mutexName), cancellationToken) + .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + await _logger.LogTraceAsync($"Mutex '{_mutexName}' sent to the process lifetime handler"); + + // Setup the server channel with the testhost controller + _pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + _logger.LogTrace($"Hang dump pipe name: '{_pipeNameDescription.Name}'"); + _singleConnectionNamedPipeServer = new(_pipeNameDescription, CallbackAsync, _environment, _logger, _task, cancellationToken); + _singleConnectionNamedPipeServer.RegisterSerializer(new GetInProgressTestsResponseSerializer(), typeof(GetInProgressTestsResponse)); + _singleConnectionNamedPipeServer.RegisterSerializer(new GetInProgressTestsRequestSerializer(), typeof(GetInProgressTestsRequest)); + _singleConnectionNamedPipeServer.RegisterSerializer(new ExitSignalActivityIndicatorTaskRequestSerializer(), typeof(ExitSignalActivityIndicatorTaskRequest)); + _singleConnectionNamedPipeServer.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + await _logger.LogTraceAsync($"Send consumer pipe name to the test controller '{_pipeNameDescription.Name}'"); + await _namedPipeClient.RequestReplyAsync(new ConsumerPipeNameRequest(_pipeNameDescription.Name), cancellationToken) + .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + + // Wait the connection from the testhost controller + await _singleConnectionNamedPipeServer.WaitConnectionAsync(cancellationToken).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + await _logger.LogTraceAsync($"Test host controller connected"); + } + catch (OperationCanceledException ex) when (ex.CancellationToken == cancellationToken) + { + // Do nothing, we're stopping + } + } + + private async Task CallbackAsync(IRequest request) + { + if (request is GetInProgressTestsRequest) + { + await _logger.LogDebugAsync($"Received '{nameof(GetInProgressTestsRequest)}'"); + return new GetInProgressTestsResponse(_testsCurrentExecutionState.Select(x => (x.Key, (int)_clock.UtcNow.Subtract(x.Value.Item2).TotalSeconds)).ToArray()); + } + else if (request is ExitSignalActivityIndicatorTaskRequest) + { + await _logger.LogDebugAsync($"Received '{nameof(ExitSignalActivityIndicatorTaskRequest)}'"); + await ExitSignalActivityIndicatorTaskAsync(); + return VoidResponse.CachedInstance; + } + else + { + throw new ArgumentOutOfRangeException(string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpUnsupportedRequestTypeErrorMessage, request.GetType().FullName)); + } + } + + public async Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested + || value is not TestNodeUpdateMessage nodeChangedMessage) + { + return; + } + + TestNodeStateProperty? state = nodeChangedMessage.TestNode.Properties.SingleOrDefault(); + if (state is InProgressTestNodeStateProperty) + { + if (_traceLevelEnabled) + { + await _logger.LogTraceAsync($"New in-progress test '{nodeChangedMessage.TestNode.DisplayName}'"); + } + + _testsCurrentExecutionState.TryAdd(nodeChangedMessage.TestNode.DisplayName, (typeof(InProgressTestNodeStateProperty), _clock.UtcNow)); + } + else if (state is PassedTestNodeStateProperty or ErrorTestNodeStateProperty or CancelledTestNodeStateProperty + or FailedTestNodeStateProperty or TimeoutTestNodeStateProperty or SkippedTestNodeStateProperty + && _testsCurrentExecutionState.TryRemove(nodeChangedMessage.TestNode.DisplayName, out (Type, DateTimeOffset) record) + && _traceLevelEnabled) + { + await _logger.LogTraceAsync($"Test removed from in-progress list '{nodeChangedMessage.TestNode.DisplayName}' after '{_clock.UtcNow.Subtract(record.Item2)}', total in-progress '{_testsCurrentExecutionState.Count}'"); + } + + // Optimization, we're interested in test progression and eventually in the discovery progression + if (state is not InProgressTestNodeStateProperty) + { + if (_traceLevelEnabled) + { + await _logger.LogTraceAsync($"Signal for action node {nodeChangedMessage.TestNode.DisplayName} - '{state}'"); + } + + // Signal the activity if it's not set + if (!_signalActivity.IsSet) + { + _signalActivity.Set(); + } + } + } + + private Task SignalActivityIndicatorAsync() + { + _activityIndicatorMutex = new Mutex(true, _mutexName); + _mutexCreated.Set(); + + while (!_exitSignalActivityIndicatorAsync) + { + // Wait for the signal + // We don't add the timeout here because depends on the user value specified with the --hangdump-timeout option + _signalActivity.Wait(); + + if (_traceLevelEnabled) + { + _logger.LogTrace($"Signal process lifetime handler, exitSignalActivityIndicatorAsync {_exitSignalActivityIndicatorAsync}"); + } + + _activityIndicatorMutex.ReleaseMutex(); + _activityIndicatorMutex.WaitOne(TimeoutHelper.DefaultHangTimeSpanTimeout); + + if (_traceLevelEnabled) + { + _logger.LogTrace($"Signaled by process lifetime handler, exitSignalActivityIndicatorAsync {_exitSignalActivityIndicatorAsync}"); + } + + // Reset the signal + _signalActivity.Reset(); + } + + _logger.LogDebug($"Exit 'SignalActivityIndicatorAsync'"); + + return Task.CompletedTask; + } + + public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, CancellationToken cancellationToken) + { + ApplicationStateGuard.Ensure(_namedPipeClient is not null); + ApplicationStateGuard.Ensure(_activityIndicatorMutex is not null); + + if (!await IsEnabledAsync()) + { + return; + } + + await _namedPipeClient.RequestReplyAsync(new SessionEndSerializerRequest(), cancellationToken) + .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + + await _logger.LogDebugAsync($"Signal for test session end'"); + await ExitSignalActivityIndicatorTaskAsync(); + + await _logger.LogTraceAsync($"Signaled by process for it's exit"); + _sessionEndCalled = true; + } + + private async Task ExitSignalActivityIndicatorTaskAsync() + { + if (_exitSignalActivityIndicatorAsync) + { + return; + } + + ApplicationStateGuard.Ensure(_signalActivityIndicatorTask is not null); + _exitSignalActivityIndicatorAsync = true; + _signalActivity.Set(); + await _signalActivityIndicatorTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + } + +#if NETCOREAPP + public async ValueTask DisposeAsync() + { + await DisposeHelper.DisposeAsync(_namedPipeClient); + + // If the OnTestSessionFinishingAsync is not called means that something unhandled happened + // and we didn't correctly coordinate the shutdown with the HangDumpProcessLifetimeHandler. + // If we go do wait for the server we will hang. + if (_sessionEndCalled) + { + await DisposeHelper.DisposeAsync(_singleConnectionNamedPipeServer); + } + + _pipeNameDescription?.Dispose(); + _mutexCreated.Dispose(); + _signalActivity.Dispose(); + _activityIndicatorMutex?.Dispose(); + } +#else + public void Dispose() + { + _namedPipeClient?.Dispose(); + + // If the OnTestSessionFinishingAsync is not called means that something unhandled happened + // and we didn't correctly coordinate the shutdown with the HangDumpProcessLifetimeHandler. + // If we go do wait for the server we will hang. + if (_sessionEndCalled) + { + _singleConnectionNamedPipeServer?.Dispose(); + } + + _pipeNameDescription?.Dispose(); + _mutexCreated.Dispose(); + _signalActivity.Dispose(); + _activityIndicatorMutex?.Dispose(); + } +#endif +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpCommandLineProvider.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpCommandLineProvider.cs new file mode 100644 index 0000000000..823de1e328 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpCommandLineProvider.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.Diagnostics.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Extensions.Diagnostics; + +internal sealed class HangDumpCommandLineProvider : ICommandLineOptionsProvider +{ + public const string HangDumpOptionName = "hangdump"; + public const string HangDumpFileNameOptionName = "hangdump-filename"; + public const string HangDumpTimeoutOptionName = "hangdump-timeout"; + public const string HangDumpTypeOptionName = "hangdump-type"; + +#if NETCOREAPP + private static readonly string[] HangDumpTypeOptions = ["Mini", "Heap", "Full", "Triage"]; +#else + private static readonly string[] HangDumpTypeOptions = ["Mini", "Heap", "Full"]; +#endif + + private static readonly IReadOnlyCollection CachedCommandLineOptions = + [ + new(HangDumpOptionName, ExtensionResources.HangDumpOptionDescription, ArgumentArity.Zero, false), + new(HangDumpTimeoutOptionName, ExtensionResources.HangDumpTimeoutOptionDescription, ArgumentArity.ExactlyOne, false), + new(HangDumpFileNameOptionName, ExtensionResources.HangDumpFileNameOptionDescription, ArgumentArity.ExactlyOne, false), + new(HangDumpTypeOptionName, ExtensionResources.HangDumpTypeOptionDescription, ArgumentArity.ExactlyOne, false) + ]; + + private readonly HangDumpConfiguration _hangDumpConfiguration; + + public HangDumpCommandLineProvider(HangDumpConfiguration hangDumpConfiguration) => _hangDumpConfiguration = hangDumpConfiguration; + + public string Uid => nameof(HangDumpCommandLineProvider); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => ExtensionResources.HangDumpExtensionDisplayName; + + public string Description => ExtensionResources.HangDumpExtensionDescription; + + public Task IsEnabledAsync() => Task.FromResult(true); + + public IReadOnlyCollection GetCommandLineOptions() => CachedCommandLineOptions; + + public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) + { + if (commandOption.Name == HangDumpTimeoutOptionName && !TimeSpanParser.TryParse(arguments[0], out TimeSpan _)) + { + return ValidationResult.InvalidTask(ExtensionResources.HangDumpTimeoutOptionInvalidArgument); + } + + if (commandOption.Name == HangDumpTypeOptionName) + { + if (!HangDumpTypeOptions.Contains(arguments[0], StringComparer.OrdinalIgnoreCase)) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpTypeOptionInvalidType, arguments[0])); + } + } + + return ValidationResult.ValidTask; + } + + public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) + => (commandLineOptions.IsOptionSet(HangDumpTimeoutOptionName) || + commandLineOptions.IsOptionSet(HangDumpFileNameOptionName) || + commandLineOptions.IsOptionSet(HangDumpTypeOptionName)) && + !commandLineOptions.IsOptionSet(HangDumpOptionName) + ? ValidationResult.InvalidTask(ExtensionResources.MissingHangDumpMainOption) + : ValidationResult.ValidTask; +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpConfiguration.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpConfiguration.cs new file mode 100644 index 0000000000..00b2633bf2 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpConfiguration.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Extensions.Diagnostics; + +internal sealed class HangDumpConfiguration +{ + public const string PipeName = "TESTINGPLATFORM_HANGDUMP_PIPENAME"; + public const string MutexName = "TESTINGPLATFORM_HANGDUMP_MUTEXNAME"; + public const string MutexNameSuffix = "TESTINGPLATFORM_HANGDUMP_MUTEXNAME_SUFFIX"; + + public HangDumpConfiguration(ITestApplicationModuleInfo testApplicationModuleInfo, PipeNameDescription pipeNameDescription, string mutexSuffix) + { + PipeNameValue = pipeNameDescription.Name; + PipeNameKey = $"{PipeName}_{FNV_1aHashHelper.ComputeStringHash(testApplicationModuleInfo.GetCurrentTestApplicationFullPath())}_{mutexSuffix}"; + MutexSuffix = mutexSuffix; + } + + public string PipeNameKey { get; } + + public string PipeNameValue { get; } + + public string MutexSuffix { get; } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpEnvironmentVariableProvider.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpEnvironmentVariableProvider.cs new file mode 100644 index 0000000000..6c855167bf --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpEnvironmentVariableProvider.cs @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.Diagnostics.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.TestHostControllers; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Extensions.Diagnostics; + +internal sealed class HangDumpEnvironmentVariableProvider : ITestHostEnvironmentVariableProvider +{ + private readonly ICommandLineOptions _commandLineOptions; + private readonly HangDumpConfiguration _hangDumpConfiguration; + + public HangDumpEnvironmentVariableProvider(ICommandLineOptions commandLineOptions, HangDumpConfiguration hangDumpConfiguration) + { + _commandLineOptions = commandLineOptions; + _hangDumpConfiguration = hangDumpConfiguration; + } + + public string Uid => nameof(HangDumpEnvironmentVariableProvider); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => ExtensionResources.HangDumpExtensionDisplayName; + + public string Description => ExtensionResources.HangDumpExtensionDescription; + + public Task IsEnabledAsync() => Task.FromResult(_commandLineOptions.IsOptionSet(HangDumpCommandLineProvider.HangDumpOptionName) && + !_commandLineOptions.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey)); + + public Task UpdateAsync(IEnvironmentVariables environmentVariables) + { + environmentVariables.SetVariable( + new(_hangDumpConfiguration.PipeNameKey, _hangDumpConfiguration.PipeNameValue, false, true)); + environmentVariables.SetVariable( + new(HangDumpConfiguration.MutexNameSuffix, _hangDumpConfiguration.MutexSuffix, false, true)); + return Task.CompletedTask; + } + + public Task ValidateTestHostEnvironmentVariablesAsync(IReadOnlyEnvironmentVariables environmentVariables) + { + if (!environmentVariables.TryGetVariable(_hangDumpConfiguration.PipeNameKey, out OwnedEnvironmentVariable? envVar)) + { + return Task.FromResult( + ValidationResult.Invalid( + string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpEnvironmentVariableIsMissingErrorMessage, _hangDumpConfiguration.PipeNameKey))); + } + + if (envVar.Value != _hangDumpConfiguration.PipeNameValue) + { + return Task.FromResult( + ValidationResult.Invalid( + string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpEnvironmentVariableInvalidValueErrorMessage, _hangDumpConfiguration.PipeNameKey, envVar.Value, _hangDumpConfiguration.PipeNameKey))); + } + + if (!environmentVariables.TryGetVariable(HangDumpConfiguration.MutexNameSuffix, out envVar)) + { + return Task.FromResult( + ValidationResult.Invalid( + string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpEnvironmentVariableIsMissingErrorMessage, HangDumpConfiguration.MutexNameSuffix))); + } + + if (envVar.Value != _hangDumpConfiguration.MutexSuffix) + { + return Task.FromResult( + ValidationResult.Invalid( + string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpEnvironmentVariableInvalidValueErrorMessage, HangDumpConfiguration.MutexNameSuffix, envVar.Value, _hangDumpConfiguration.MutexSuffix))); + } + + // No problem found + return ValidationResult.ValidTask; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpExtensions.cs new file mode 100644 index 0000000000..8e3fa6d96d --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpExtensions.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.Diagnostics; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Extensions; + +public static class HangDumpExtensions +{ + public static void AddHangDumpProvider(this ITestApplicationBuilder builder) + { + CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(new SystemEnvironment(), new SystemProcessHandler()); + string mutexSuffix = Guid.NewGuid().ToString("N"); + PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + HangDumpConfiguration hangDumpConfiguration = new(testApplicationModuleInfo, pipeNameDescription, mutexSuffix); + + builder.TestHostControllers.AddProcessLifetimeHandler(serviceProvider + => new HangDumpProcessLifetimeHandler( + hangDumpConfiguration, + pipeNameDescription, + serviceProvider.GetMessageBus(), + serviceProvider.GetOutputDevice(), + serviceProvider.GetCommandLineOptions(), + serviceProvider.GetTask(), + serviceProvider.GetEnvironment(), + serviceProvider.GetLoggerFactory(), + serviceProvider.GetTestApplicationModuleInfo(), + serviceProvider.GetConfiguration(), + serviceProvider.GetProcessHandler(), + serviceProvider, + serviceProvider.GetClock())); + + builder.TestHostControllers.AddEnvironmentVariableProvider(serviceProvider + => new HangDumpEnvironmentVariableProvider(serviceProvider.GetCommandLineOptions(), hangDumpConfiguration)); + + builder.CommandLine.AddProvider(() + => new HangDumpCommandLineProvider(hangDumpConfiguration)); + + var hangDumpActivityIndicatorComposite + = new CompositeExtensionFactory(serviceProvider => new HangDumpActivityIndicator( + serviceProvider.GetCommandLineOptions(), + serviceProvider.GetEnvironment(), + serviceProvider.GetTask(), + serviceProvider.GetTestApplicationModuleInfo(), + serviceProvider.GetLoggerFactory(), + serviceProvider.GetClock())); + + builder.TestHost.AddDataConsumer(hangDumpActivityIndicatorComposite); + builder.TestHost.AddTestSessionLifetimeHandle(hangDumpActivityIndicatorComposite); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs new file mode 100644 index 0000000000..9a4a823a32 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs @@ -0,0 +1,429 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.Diagnostics.Resources; +using Microsoft.Testing.Extensions.HangDump.Serializers; +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestHostControllers; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Models; +using Microsoft.Testing.Platform.IPC.Serializers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Services; + +#if NETCOREAPP +using Microsoft.Diagnostics.NETCore.Client; +#endif + +namespace Microsoft.Testing.Extensions.Diagnostics; + +internal sealed class HangDumpProcessLifetimeHandler : ITestHostProcessLifetimeHandler, IOutputDeviceDataProducer, IDataProducer, +#if NETCOREAPP + IAsyncDisposable +#else + IDisposable +#endif +{ + private readonly HangDumpConfiguration _hangDumpConfiguration; + private readonly IMessageBus _messageBus; + private readonly IOutputDevice _outputDisplay; + private readonly ICommandLineOptions _commandLineOptions; + private readonly ITask _task; + private readonly IEnvironment _environment; + private readonly IConfiguration _configuration; + private readonly IProcessHandler _processHandler; + private readonly IServiceProvider _serviceProvider; + private readonly IClock _clock; + private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource; + private readonly PipeNameDescription _pipeNameDescription; + private readonly bool _traceEnabled; + private readonly ILogger _logger; + private readonly ManualResetEventSlim _mutexNameReceived = new(false); + private readonly ManualResetEventSlim _waitConsumerPipeName = new(false); + + private TimeSpan _activityTimerValue = TimeSpan.FromMinutes(30); + private Task? _waitConnectionTask; + private Task? _activityIndicatorTask; + private NamedPipeServer? _singleConnectionNamedPipeServer; + private string? _activityTimerMutexName; + private bool _exitActivityIndicatorTask; + private string _dumpType = "Full"; + private string _dumpFileNamePattern; + private Mutex? _activityIndicatorMutex; + private ITestHostProcessInformation? _testHostProcessInformation; + private string _dumpFileTaken = string.Empty; + private NamedPipeClient? _namedPipeClient; + + public HangDumpProcessLifetimeHandler( + HangDumpConfiguration hangDumpConfiguration, + PipeNameDescription pipeNameDescription, + IMessageBus messageBus, + IOutputDevice outputDisplay, + ICommandLineOptions commandLineOptions, + ITask task, + IEnvironment environment, + ILoggerFactory loggerFactory, + ITestApplicationModuleInfo testApplicationModuleInfo, + IConfiguration configuration, + IProcessHandler processHandler, + IServiceProvider serviceProvider, + IClock clock) + { + _logger = loggerFactory.CreateLogger(); + _traceEnabled = _logger.IsEnabled(LogLevel.Trace); + _hangDumpConfiguration = hangDumpConfiguration; + _pipeNameDescription = pipeNameDescription; + _messageBus = messageBus; + _outputDisplay = outputDisplay; + _commandLineOptions = commandLineOptions; + _task = task; + _environment = environment; + _configuration = configuration; + _processHandler = processHandler; + _serviceProvider = serviceProvider; + _clock = clock; + _testApplicationCancellationTokenSource = serviceProvider.GetTestApplicationCancellationTokenSource(); + _dumpFileNamePattern = $"{Path.GetFileNameWithoutExtension(testApplicationModuleInfo.GetCurrentTestApplicationFullPath())}_%p_hang.dmp"; + } + + public string Uid => nameof(HangDumpProcessLifetimeHandler); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => ExtensionResources.HangDumpExtensionDisplayName; + + public string Description => ExtensionResources.HangDumpExtensionDescription; + + public Type[] DataTypesProduced => [typeof(FileArtifact)]; + + public Task IsEnabledAsync() => Task.FromResult(_commandLineOptions.IsOptionSet(HangDumpCommandLineProvider.HangDumpOptionName) && + !_commandLineOptions.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey)); + + public async Task BeforeTestHostProcessStartAsync(CancellationToken cancellationToken) + { + if (_commandLineOptions.TryGetOptionArgumentList(HangDumpCommandLineProvider.HangDumpTimeoutOptionName, out string[]? timeout)) + { + _activityTimerValue = TimeSpanParser.Parse(timeout[0]); + } + + if (_commandLineOptions.TryGetOptionArgumentList(HangDumpCommandLineProvider.HangDumpTypeOptionName, out string[]? dumpType)) + { + _dumpType = dumpType[0]; + } + + if (_commandLineOptions.TryGetOptionArgumentList(HangDumpCommandLineProvider.HangDumpFileNameOptionName, out string[]? fileName)) + { + _dumpFileNamePattern = fileName[0]; + } + + await _logger.LogInformationAsync($"Hang dump timeout setup {_activityTimerValue}."); + + _waitConnectionTask = _task.Run( + async () => + { + _singleConnectionNamedPipeServer = new(_pipeNameDescription, CallbackAsync, _environment, _logger, _task, cancellationToken); + _singleConnectionNamedPipeServer.RegisterSerializer(new ActivityIndicatorMutexNameRequestSerializer(), typeof(ActivityIndicatorMutexNameRequest)); + _singleConnectionNamedPipeServer.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + _singleConnectionNamedPipeServer.RegisterSerializer(new SessionEndSerializerRequestSerializer(), typeof(SessionEndSerializerRequest)); + _singleConnectionNamedPipeServer.RegisterSerializer(new ConsumerPipeNameRequestSerializer(), typeof(ConsumerPipeNameRequest)); + await _logger.LogDebugAsync($"Waiting for connection to {_singleConnectionNamedPipeServer.PipeName.Name}"); + await _singleConnectionNamedPipeServer.WaitConnectionAsync(cancellationToken).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + }, cancellationToken); + } + + private async Task CallbackAsync(IRequest request) + { + if (request is ActivityIndicatorMutexNameRequest activityIndicatorMutexNameRequest) + { + await _logger.LogDebugAsync($"Mutex name received by the test host, '{activityIndicatorMutexNameRequest.MutexName}'"); + _activityTimerMutexName = activityIndicatorMutexNameRequest.MutexName; + _mutexNameReceived.Set(); + return VoidResponse.CachedInstance; + } + else if (request is SessionEndSerializerRequest) + { + await _logger.LogDebugAsync($"Session end received by the test host"); + _exitActivityIndicatorTask = true; +#if NET + if (_namedPipeClient is not null) + { + await _namedPipeClient.DisposeAsync(); + } +#else + _namedPipeClient?.Dispose(); +#endif + return VoidResponse.CachedInstance; + } + else if (request is ConsumerPipeNameRequest consumerPipeNameRequest) + { + await _logger.LogDebugAsync($"Consumer pipe name received '{consumerPipeNameRequest.PipeName}'"); + _namedPipeClient = new NamedPipeClient(consumerPipeNameRequest.PipeName); + _namedPipeClient.RegisterSerializer(new GetInProgressTestsResponseSerializer(), typeof(GetInProgressTestsResponse)); + _namedPipeClient.RegisterSerializer(new GetInProgressTestsRequestSerializer(), typeof(GetInProgressTestsRequest)); + _namedPipeClient.RegisterSerializer(new ExitSignalActivityIndicatorTaskRequestSerializer(), typeof(ExitSignalActivityIndicatorTaskRequest)); + _namedPipeClient.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + _waitConsumerPipeName.Set(); + return VoidResponse.CachedInstance; + } + else + { + throw new ArgumentOutOfRangeException(string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpUnsupportedRequestTypeErrorMessage, request)); + } + } + + public async Task OnTestHostProcessStartedAsync(ITestHostProcessInformation testHostProcessInformation, CancellationToken cancellation) + { + ApplicationStateGuard.Ensure(_waitConnectionTask is not null); + ApplicationStateGuard.Ensure(_singleConnectionNamedPipeServer is not null); + try + { + _testHostProcessInformation = testHostProcessInformation; + + await _logger.LogDebugAsync($"Wait for test host connection to the server pipe '{_singleConnectionNamedPipeServer.PipeName.Name}'"); + await _waitConnectionTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + using CancellationTokenSource timeout = new(TimeoutHelper.DefaultHangTimeSpanTimeout); + using var linkedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellation, timeout.Token); + _waitConsumerPipeName.Wait(linkedCancellationToken.Token); + ApplicationStateGuard.Ensure(_namedPipeClient is not null); + await _namedPipeClient.ConnectAsync(cancellation).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + await _logger.LogDebugAsync($"Connected to the test host server pipe '{_namedPipeClient.PipeName}'"); + + // Keep the custom thread to avoid to waste one from thread pool. + _activityIndicatorTask = _task.RunLongRunning(ActivityTimerAsync, "[HangDump] ActivityTimerAsync", cancellation); + } + catch (OperationCanceledException) when (cancellation.IsCancellationRequested) + { + return; + } + } + + public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testHostProcessInformation, CancellationToken cancellation) + { + if (cancellation.IsCancellationRequested) + { + return; + } + + if (!testHostProcessInformation.HasExitedGracefully) + { + _logger.LogDebug($"Testhost didn't exit gracefully '{testHostProcessInformation.ExitCode}', disposing _activityIndicatorMutex(is null: '{_activityIndicatorMutex is null}')"); + _activityIndicatorMutex?.Dispose(); + } + + if (!RoslynString.IsNullOrEmpty(_dumpFileTaken)) + { + await _messageBus.PublishAsync(this, new FileArtifact(new FileInfo(_dumpFileTaken), ExtensionResources.HangDumpArtifactDisplayName, ExtensionResources.HangDumpArtifactDescription)); + } + } + + private async Task ActivityTimerAsync() + { + _logger.LogDebug($"Wait for mutex name from the test host"); + + if (!_mutexNameReceived.Wait(TimeoutHelper.DefaultHangTimeSpanTimeout)) + { + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ExtensionResources.MutexNameReceptionTimeoutErrorMessage, TimeoutHelper.DefaultHangTimeoutSeconds)); + } + + ApplicationStateGuard.Ensure(_activityTimerMutexName is not null); + + _logger.LogDebug($"Open activity mutex '{_activityTimerMutexName}'"); + + if (!Mutex.TryOpenExisting(_activityTimerMutexName, out _activityIndicatorMutex)) + { + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ExtensionResources.MutexDoesNotExistErrorMessage, _activityTimerMutexName)); + } + + bool timeoutFired = false; + try + { + // Don't wait in async in the while, we need thread affinity for the mutex + while (true) + { + if (_traceEnabled) + { + _logger.LogTrace($"Wait for activity signal"); + } + + if (!_activityIndicatorMutex.WaitOne(_activityTimerValue)) + { + timeoutFired = true; + break; + } + + if (_traceEnabled) + { + _logger.LogTrace($"Activity signal received by the test host '{_clock.UtcNow}'"); + } + + // We don't release in case of exit because we will release after the timeout check to unblock the client and exit the task + if (_exitActivityIndicatorTask) + { + break; + } + else + { + _activityIndicatorMutex.ReleaseMutex(); + } + } + + if (_traceEnabled) + { + _logger.LogTrace($"Exit 'ActivityTimerAsync'"); + } + } + catch (AbandonedMutexException) + { + // If the mutex is abandoned from the test host crash we will get an exception + _logger.LogDebug($"Mutex '{_activityTimerMutexName}' is abandoned"); + } + catch (ObjectDisposedException) + { + // If test host exit in a non gracefully way on process exit we dispose the mutex to unlock the activity timer. + // In this way we release also the dispose. + _logger.LogDebug($"Mutex '{_activityTimerMutexName}' is disposed"); + } + + if (!timeoutFired) + { + try + { + _logger.LogDebug($"Timeout is not fired release activity mutex handle to allow test host to close"); + _activityIndicatorMutex.ReleaseMutex(); + } + catch (AbandonedMutexException) + { + // If the mutex is abandoned from the test host crash we will get an exception + _logger.LogDebug($"Mutex '{_activityTimerMutexName}' is abandoned, during last release"); + } + catch (ObjectDisposedException) + { + // If test host exit in a non gracefully way on process exit we dispose the mutex to unlock the activity timer. + _logger.LogDebug($"Mutex '{_activityTimerMutexName}' is disposed, during last release"); + } + } + + _activityIndicatorMutex.Dispose(); + _logger.LogDebug($"Activity indicator disposed"); + + if (timeoutFired) + { + await TakeDumpAsync(); + } + } + + private async Task TakeDumpAsync() + { + ApplicationStateGuard.Ensure(_testHostProcessInformation is not null); + ApplicationStateGuard.Ensure(_dumpType is not null); + + await _logger.LogInformationAsync($"Hang dump timeout({_activityTimerValue}) expired."); + await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpTimeoutExpired, _activityTimerValue))); + + string finalDumpFileName = _dumpFileNamePattern.Replace("%p", _testHostProcessInformation.PID.ToString(CultureInfo.InvariantCulture)); + finalDumpFileName = Path.Combine(_configuration.GetTestResultDirectory(), finalDumpFileName); + + ApplicationStateGuard.Ensure(_namedPipeClient is not null); + GetInProgressTestsResponse tests = await _namedPipeClient.RequestReplyAsync(new GetInProgressTestsRequest(), _testApplicationCancellationTokenSource.CancellationToken); + await _namedPipeClient.RequestReplyAsync(new ExitSignalActivityIndicatorTaskRequest(), _testApplicationCancellationTokenSource.CancellationToken); + if (tests.Tests.Length > 0) + { + string hangTestsFileName = Path.Combine(_configuration.GetTestResultDirectory(), Path.ChangeExtension(Path.GetFileName(finalDumpFileName), ".log")); + using (FileStream fs = File.OpenWrite(hangTestsFileName)) + using (StreamWriter sw = new(fs)) + { + await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(ExtensionResources.RunningTestsWhileDumping)); + foreach ((string testName, int seconds) in tests.Tests) + { + await sw.WriteLineAsync($"[{TimeSpan.FromSeconds(seconds)}] {testName}"); + await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText($"[{TimeSpan.FromSeconds(seconds)}] {testName}")); + } + } + + await _messageBus.PublishAsync(this, new FileArtifact(new FileInfo(hangTestsFileName), ExtensionResources.HangTestListArtifactDisplayName, ExtensionResources.HangTestListArtifactDescription)); + } + + await _logger.LogInformationAsync($"Creating dump filename {finalDumpFileName}"); + + await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(string.Format(CultureInfo.InvariantCulture, ExtensionResources.CreatingDumpFile, finalDumpFileName))); + +#if NETCOREAPP + DiagnosticsClient diagnosticsClient = new(_testHostProcessInformation.PID); + DumpType dumpType = _dumpType.ToLowerInvariant().Trim() switch + { + "mini" => DumpType.Normal, + "heap" => DumpType.WithHeap, + "triage" => DumpType.Triage, + "full" => DumpType.Full, + _ => throw ApplicationStateGuard.Unreachable(), + }; + + diagnosticsClient.WriteDump(dumpType, finalDumpFileName, true); + NotifyCrashDumpServiceIfEnabled(); + using IProcess process = _processHandler.GetProcessById(_testHostProcessInformation.PID); + process.Kill(); + await process.WaitForExitAsync(); + +#else + MiniDumpWriteDump.MiniDumpTypeOption miniDumpTypeOption = _dumpType.ToLowerInvariant().Trim() switch + { + "mini" => MiniDumpWriteDump.MiniDumpTypeOption.Mini, + "heap" => MiniDumpWriteDump.MiniDumpTypeOption.Heap, + "full" => MiniDumpWriteDump.MiniDumpTypeOption.Full, + _ => throw ApplicationStateGuard.Unreachable(), + }; + + MiniDumpWriteDump.CollectDumpUsingMiniDumpWriteDump(_testHostProcessInformation.PID, finalDumpFileName, miniDumpTypeOption); + NotifyCrashDumpServiceIfEnabled(); + using IProcess process = _processHandler.GetProcessById(_testHostProcessInformation.PID); + process.Kill(); + process.WaitForExit(); +#endif + _dumpFileTaken = finalDumpFileName; + } + + private static void NotifyCrashDumpServiceIfEnabled() + => AppDomain.CurrentDomain.SetData("ProcessKilledByHangDump", "true"); + + public void Dispose() + { + if (_activityIndicatorTask is not null) + { + if (!_activityIndicatorTask.Wait(TimeoutHelper.DefaultHangTimeSpanTimeout)) + { + throw new InvalidOperationException($"_activityIndicatorTask didn't exit in {TimeoutHelper.DefaultHangTimeSpanTimeout} seconds"); + } + } + + _namedPipeClient?.Dispose(); + _waitConsumerPipeName.Dispose(); + _mutexNameReceived.Dispose(); + _singleConnectionNamedPipeServer?.Dispose(); + _pipeNameDescription.Dispose(); + } + +#if NETCOREAPP + public async ValueTask DisposeAsync() + { + if (_activityIndicatorTask is not null) + { + await _activityIndicatorTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + } + + _namedPipeClient?.Dispose(); + _waitConsumerPipeName.Dispose(); + _mutexNameReceived.Dispose(); + _singleConnectionNamedPipeServer?.Dispose(); + _pipeNameDescription.Dispose(); + } +#endif +} diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/FNV1HashHelper.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Helpers/FNV1HashHelper.cs similarity index 100% rename from src/Platform/Microsoft.Testing.Platform/Helpers/FNV1HashHelper.cs rename to src/Platform/Microsoft.Testing.Extensions.HangDump/Helpers/FNV1HashHelper.cs diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Microsoft.Testing.Extensions.HangDump.csproj b/src/Platform/Microsoft.Testing.Extensions.HangDump/Microsoft.Testing.Extensions.HangDump.csproj new file mode 100644 index 0000000000..9abaf2d2d5 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Microsoft.Testing.Extensions.HangDump.csproj @@ -0,0 +1,70 @@ + + + + netstandard2.0;$(MicrosoftTestingTargetFrameworks) + Microsoft.Testing.Extensions.Diagnostics + + + + + + + + + + + + + + + + + + + + true + buildMultiTargeting + + + buildTransitive/$(TargetFramework) + + + build/$(TargetFramework) + + + + + + + + + + + + + + True + True + ExtensionResources.resx + + + True + True + ExtensionResources.resx + + + + + + + + + + ResXFileCodeGenerator + ExtensionResources.Designer.cs + + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/PACKAGE.md b/src/Platform/Microsoft.Testing.Extensions.HangDump/PACKAGE.md new file mode 100644 index 0000000000..776c34d531 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/PACKAGE.md @@ -0,0 +1,9 @@ +# Microsoft.Testing + +Microsoft Testing is a set of platform, framework and protocol intended to make it possible to run any test on any target or device. + +Documentation can be found at . + +## About + +This package provides Windows Hang dump extensions to Microsoft Testing Platform. diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.HangDump/PublicAPI/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..9a6866965b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/PublicAPI/PublicAPI.Shipped.txt @@ -0,0 +1,5 @@ +#nullable enable +Microsoft.Testing.Extensions.HangDumpExtensions +Microsoft.Testing.Extensions.HangDump.TestingPlatformBuilderHook +static Microsoft.Testing.Extensions.HangDump.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! _) -> void +static Microsoft.Testing.Extensions.HangDumpExtensions.AddHangDumpProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.HangDump/PublicAPI/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/ExtensionResources.Designer.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/ExtensionResources.Designer.cs new file mode 100644 index 0000000000..c96af79b89 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/ExtensionResources.Designer.cs @@ -0,0 +1,252 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Testing.Extensions.Diagnostics.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ExtensionResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ExtensionResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Testing.Extensions.Diagnostics.Resources.ExtensionResources", typeof(ExtensionResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Creating dump file '{0}'. + /// + internal static string CreatingDumpFile { + get { + return ResourceManager.GetString("CreatingDumpFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The hang dump file. + /// + internal static string HangDumpArtifactDescription { + get { + return ResourceManager.GetString("HangDumpArtifactDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hang dump file. + /// + internal static string HangDumpArtifactDisplayName { + get { + return ResourceManager.GetString("HangDumpArtifactDisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Environment variable '{0}' is set to '{1}' instead of '{2}'. + /// + internal static string HangDumpEnvironmentVariableInvalidValueErrorMessage { + get { + return ResourceManager.GetString("HangDumpEnvironmentVariableInvalidValueErrorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Environment variable '{0}' is not set. + /// + internal static string HangDumpEnvironmentVariableIsMissingErrorMessage { + get { + return ResourceManager.GetString("HangDumpEnvironmentVariableIsMissingErrorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Produce hang dump files when a test execution exceed a given time.. + /// + internal static string HangDumpExtensionDescription { + get { + return ResourceManager.GetString("HangDumpExtensionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hang dump. + /// + internal static string HangDumpExtensionDisplayName { + get { + return ResourceManager.GetString("HangDumpExtensionDisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specify the name of the dump file. + /// + internal static string HangDumpFileNameOptionDescription { + get { + return ResourceManager.GetString("HangDumpFileNameOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generate a dump file if the test process hangs. + /// + internal static string HangDumpOptionDescription { + get { + return ResourceManager.GetString("HangDumpOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hang dump timeout of '{0}' expired. + /// + internal static string HangDumpTimeoutExpired { + get { + return ResourceManager.GetString("HangDumpTimeoutExpired", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m.. + /// + internal static string HangDumpTimeoutOptionDescription { + get { + return ResourceManager.GetString("HangDumpTimeoutOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '--hangdump-timeout' expects a single timeout argument. + /// + internal static string HangDumpTimeoutOptionInvalidArgument { + get { + return ResourceManager.GetString("HangDumpTimeoutOptionInvalidArgument", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full'. + /// + internal static string HangDumpTypeOptionDescription { + get { + return ResourceManager.GetString("HangDumpTypeOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full'. + /// + internal static string HangDumpTypeOptionInvalidType { + get { + return ResourceManager.GetString("HangDumpTypeOptionInvalidType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Request of type '{0}' is not supported. + /// + internal static string HangDumpUnsupportedRequestTypeErrorMessage { + get { + return ResourceManager.GetString("HangDumpUnsupportedRequestTypeErrorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The list of tests that were running at the time of the hang. + /// + internal static string HangTestListArtifactDescription { + get { + return ResourceManager.GetString("HangTestListArtifactDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hang test list. + /// + internal static string HangTestListArtifactDisplayName { + get { + return ResourceManager.GetString("HangTestListArtifactDisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line. + /// + internal static string MissingHangDumpMainOption { + get { + return ResourceManager.GetString("MissingHangDumpMainOption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot find mutex '{0}'. + /// + internal static string MutexDoesNotExistErrorMessage { + get { + return ResourceManager.GetString("MutexDoesNotExistErrorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mutex name wasn't received after '{0}' seconds. + /// + internal static string MutexNameReceptionTimeoutErrorMessage { + get { + return ResourceManager.GetString("MutexNameReceptionTimeoutErrorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>):. + /// + internal static string RunningTestsWhileDumping { + get { + return ResourceManager.GetString("RunningTestsWhileDumping", resourceCulture); + } + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/ExtensionResources.resx b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/ExtensionResources.resx new file mode 100644 index 0000000000..f45192fefc --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/ExtensionResources.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Creating dump file '{0}' + + + The hang dump file + + + Hang dump file + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + + + Environment variable '{0}' is not set + + + Produce hang dump files when a test execution exceed a given time. + + + Hang dump + + + Specify the name of the dump file + + + Generate a dump file if the test process hangs + + + Hang dump timeout of '{0}' expired + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + + + '--hangdump-timeout' expects a single timeout argument + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + + + Request of type '{0}' is not supported + + + The list of tests that were running at the time of the hang + + + Hang test list + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + + + Cannot find mutex '{0}' + + + Mutex name wasn't received after '{0}' seconds + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.cs.xlf new file mode 100644 index 0000000000..112c1e3635 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.cs.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + Vytváří se soubor výpisu paměti {0}. + + + + The hang dump file + Soubor výpisu paměti při zablokování + + + + Hang dump file + Soubor výpisu paměti při zablokování + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + Proměnná prostředí {0} je nastavená na {1} místo na {2}. + + + + Environment variable '{0}' is not set + Není nastavená proměnná prostředí {0}. + + + + Produce hang dump files when a test execution exceed a given time. + Vytvoří soubory výpisu paměti při zablokování, když provádění testu překročí danou dobu. + + + + Hang dump + Výpis paměti při zablokování + + + + Specify the name of the dump file + Zadejte název souboru výpisu paměti. + + + + Generate a dump file if the test process hangs + Vygeneruje soubor výpisu paměti, pokud testovací proces přestane reagovat. + + + + Hang dump timeout of '{0}' expired + Vypršel časový limit {0} pro výpis paměti při zablokování. + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + Zadejte časový limit, po kterém bude vygenerován výpis paměti. Hodnota časového limitu se zadává v jednom z následujících formátů: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Výchozí hodnota je 30m. + + + + '--hangdump-timeout' expects a single timeout argument + --hangdump-timeout očekává jeden argument časového limitu. + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + Zadejte typ výpisu paměti. Platné hodnoty jsou Mini, Heap, Triage (dostupné pouze v .NET 6+) nebo Full. Výchozí typ je Full. + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + {0} není platný typ výpisu paměti. Platné možnosti jsou Mini, Heap, Triage (k dispozici pouze v .NET 6+) a Full. + + + + Request of type '{0}' is not supported + Žádost typu {0} není podporována. + + + + The list of tests that were running at the time of the hang + Seznam testů spuštěných v době zablokování + + + + Hang test list + Seznam testů při zablokování + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + Zadali jste jeden nebo více parametrů výpisu stavu systému, ale nepovolili jste ho. Přidejte do příkazového řádku parametr --hangdump + + + + Cannot find mutex '{0}' + Objekt mutex {0} nebyl nalezen. + + + + Mutex name wasn't received after '{0}' seconds + Název objektu mutex nebyl přijat po {0} s. + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + Při pořízení výpisu paměti stále běžely následující testy (formát: [<čas-uplynulý-od-spuštění>] <název>): + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.de.xlf new file mode 100644 index 0000000000..05215a521e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.de.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + Speicherabbilddatei "{0}" wird erstellt + + + + The hang dump file + Die Absturzspeicherabbilddatei + + + + Hang dump file + Absturzspeicherabbilddatei + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + Umgebungsvariable "{0}" ist auf "{1}" anstatt auf "{2}" festgelegt. + + + + Environment variable '{0}' is not set + Die Umgebungsvariable "{0}" wurde nicht festgelegt + + + + Produce hang dump files when a test execution exceed a given time. + Absturzspeicherabbilddateien erstellen, wenn eine Testausführung eine angegebene Zeit überschreitet. + + + + Hang dump + Absturzspeicherabbild + + + + Specify the name of the dump file + Namen der Speicherabbilddatei angeben + + + + Generate a dump file if the test process hangs + Speicherabbilddatei generieren, wenn der Testprozess abstürzt + + + + Hang dump timeout of '{0}' expired + Timeout für Absturzspeicherabbild von "{0}" abgelaufen + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + Geben Sie das Timeout an, nach dem das Speicherabbild generiert wird. Der Timeoutwert wird in einem der folgenden Formate angegeben: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Der Standardwert ist 30m. + + + + '--hangdump-timeout' expects a single timeout argument + "--hangdump-timeout" erwartet ein einzelnes Timeoutargument. + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + Geben Sie den Typ des Speicherabbilds an. Gültige Werte sind "Mini", "Heap", "Triage" (nur in .NET 6 und höher verfügbar) oder "Full". Der Standardtyp ist "Full". + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + "{0}" ist kein gültiger Speicherabbildtyp. Gültige Optionen sind "Mini", "Heap", "Triage" (nur in .NET 6 und höher verfügbar) und "Full". + + + + Request of type '{0}' is not supported + Anforderung vom Typ "{0}" wird nicht unterstützt + + + + The list of tests that were running at the time of the hang + Die Liste der Tests, die zum Zeitpunkt des Absturzes ausgeführt wurden. + + + + Hang test list + Liste der Tests bei Absturz + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + Sie haben mindestens einen Parameter für das Absturzabbild angegeben, aber nicht aktiviert. Fügen Sie der Befehlszeile „--hangdump“ hinzu. + + + + Cannot find mutex '{0}' + Mutex "{0}" wurde nicht gefunden + + + + Mutex name wasn't received after '{0}' seconds + Der Mutexname wurde nach "{0}" Sekunden nicht empfangen. + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + Die folgenden Tests wurden noch ausgeführt, als das Speicherabbild erstellt wurde (Format: [<time-elapsed-since-start>] <name>): + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.es.xlf new file mode 100644 index 0000000000..8425c1a4a4 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.es.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + Creando archivo de volcado '{0}' + + + + The hang dump file + El archivo de volcado de memoria + + + + Hang dump file + Archivo de volcado de bloqueo + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + La variable de entorno '{0}' se establece en '{1}' en lugar de '{2}' + + + + Environment variable '{0}' is not set + La variable de entorno '{0}' no está establecida + + + + Produce hang dump files when a test execution exceed a given time. + Genere archivos de volcado de memoria cuando una ejecución de prueba supere un tiempo determinado. + + + + Hang dump + Volcado de memoria + + + + Specify the name of the dump file + Especificar el nombre del archivo de volcado + + + + Generate a dump file if the test process hangs + Generar un archivo de volcado si el proceso de prueba se bloquea + + + + Hang dump timeout of '{0}' expired + Tiempo de espera de volcado de bloqueo de '{0}' expirado + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + Especifique el tiempo de espera después del cual se generará el volcado. El valor de tiempo de espera se especifica en uno de los formatos siguientes: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. El valor predeterminado es 30 m. + + + + '--hangdump-timeout' expects a single timeout argument + '--hangdump-timeout' espera un único argumento de tiempo de espera + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + Especifique el tipo de volcado. Los valores válidos son "Mini", "Heap", "Triage" (solo disponible en .NET 6+) o "Full". El tipo predeterminado es 'Full' + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + '{0}' no es un tipo de volcado válido. Las opciones válidas son "Mini", "Montón", "Evaluación de prioridades" (solo disponible en .NET 6+) y "Full" + + + + Request of type '{0}' is not supported + No se admite la solicitud de tipo '{0}' + + + + The list of tests that were running at the time of the hang + Lista de pruebas que se estaban ejecutando en el momento del bloqueo + + + + Hang test list + Lista de pruebas de bloqueo + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + Ha especificado uno o varios parámetros de volcado de memoria, pero no los ha habilitado. Agregue --hangdump a la línea de comandos. + + + + Cannot find mutex '{0}' + No se encuentra la exclusión mutua '{0}' + + + + Mutex name wasn't received after '{0}' seconds + No se recibió el nombre de exclusión mutua después de '{0}' segundos + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + Las siguientes pruebas aún se estaban ejecutando cuando se realizó el volcado (formato: [<time-elapsed-since-start>] <name>): + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.fr.xlf new file mode 100644 index 0000000000..93e8e8ab43 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.fr.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + Création du fichier de vidage «{0}» + + + + The hang dump file + Fichier de vidage de blocage + + + + Hang dump file + Bloquer le fichier de vidage + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + La variable d’environnement «{0}» a la valeur «{1}» au lieu de «{2}» + + + + Environment variable '{0}' is not set + La variable d’environnement '{0}' n’est pas définie + + + + Produce hang dump files when a test execution exceed a given time. + Produire des fichiers de vidage de blocage lorsqu’une exécution de test dépasse un délai donné. + + + + Hang dump + Blocage de l’image mémoire + + + + Specify the name of the dump file + Spécifier le nom du fichier de vidage + + + + Generate a dump file if the test process hangs + Générer un fichier de vidage si le processus de test se bloque + + + + Hang dump timeout of '{0}' expired + Expiration du délai d’expiration du vidage du blocage de «{0}» + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + Spécifiez le délai d’expiration après lequel le vidage sera généré. La valeur du délai d'attente est spécifiée dans l'un des formats suivants : 1,5 h, 1,5 heure, 1,5 heure, 90 m, 90 min, 90 minutes, 90 minutes 5 400 s, 5 400 s, 5 400 secondes, 5 400 secondes. La valeur par défaut est 30 m. + + + + '--hangdump-timeout' expects a single timeout argument + '--hangdump-timeout' attend un seul argument de délai d'attente + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + Spécifiez le type de l’image mémoire. Les valeurs valides sont « Mini », « Heap », « Triage » (uniquement disponible dans .NET 6+) ou « Full ». Le type par défaut est ' Full' + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + '{0}' n’est pas un type de vidage valide. Les options valides sont « Mini », « Heap », « Triage » (uniquement disponible dans .NET 6+) et « Full » + + + + Request of type '{0}' is not supported + La requête de type '{0}' n’est pas prise en charge + + + + The list of tests that were running at the time of the hang + Liste des tests en cours d’exécution au moment du blocage + + + + Hang test list + Bloquer la liste de tests + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + Vous avez spécifié un ou plusieurs paramètres de vidage sur blocage, mais vous ne l’avez pas activé. Ajoutez --hangdump à la ligne de commande + + + + Cannot find mutex '{0}' + Impossible de trouver le mutex '{0}' + + + + Mutex name wasn't received after '{0}' seconds + Le nom du mutex n'a pas été reçu après « {0} » secondes + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + Les tests suivants étaient toujours en cours d'exécution au moment du vidage (format : [<time-elapsed-since-start>] <name>) : + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.it.xlf new file mode 100644 index 0000000000..380c07d2a5 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.it.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + Creazione del file di dump '{0}' + + + + The hang dump file + Il file di dump di blocco + + + + Hang dump file + File dump di blocco + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + La variabile di ambiente '{0}' è impostata su '{1}' anziché su '{2}' + + + + Environment variable '{0}' is not set + La variabile di ambiente '{0}' non è impostata + + + + Produce hang dump files when a test execution exceed a given time. + Genera file di dump di blocco quando l'esecuzione di un test supera un determinato periodo di tempo. + + + + Hang dump + Dump di blocco + + + + Specify the name of the dump file + Specificare il nome del file di dump + + + + Generate a dump file if the test process hangs + Generare un file di dump se il processo di test si blocca + + + + Hang dump timeout of '{0}' expired + Timeout dump di blocco di '{0}' scaduto + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + Specificare il timeout dopo il quale verrà generato il dump. Il valore di timeout è specificato in uno dei seguenti formati: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Il valore predefinito è 30m. + + + + '--hangdump-timeout' expects a single timeout argument + '--hangdump-timeout' prevede un singolo argomento di timeout + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + Specificare il tipo di dump. I valori ammessi sono 'Mini', 'Heap', 'Triage' (disponibile solo in .NET 6+) o 'Full'. Il tipo predefinito è 'Full' + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + '{0}' non è un tipo di dump valido. Le opzioni valide sono 'Mini', 'Heap', 'Triage' (disponibile solo in .NET 6+) e 'Full'. + + + + Request of type '{0}' is not supported + La richiesta di tipo '{0}' non è supportata + + + + The list of tests that were running at the time of the hang + Elenco di test in esecuzione al momento del blocco + + + + Hang test list + Blocca elenco dei test + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + Sono stati specificati uno o più parametri del dump di blocco ma non sono stati abilitati. Aggiungere --hangdump alla riga di comando + + + + Cannot find mutex '{0}' + Non è possibile trovare il mutex '{0}' + + + + Mutex name wasn't received after '{0}' seconds + Il nome del mutex non è stato ricevuto dopo '{0}' secondi + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + I seguenti test erano ancora in esecuzione quando è stato eseguito il dump (formato: [<time-elapsed-since-start>] <name>): + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ja.xlf new file mode 100644 index 0000000000..94c5b0ac56 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ja.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + ダンプ ファイル '{0}' を作成しています + + + + The hang dump file + ハング ダンプ ファイル + + + + Hang dump file + ハング ダンプ ファイル + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + 環境変数 '{0}' は '{2}' ではなく '{1}' に設定されています + + + + Environment variable '{0}' is not set + 環境変数 '{0}' が設定されていません + + + + Produce hang dump files when a test execution exceed a given time. + テストの実行が指定された時間を超えたときにハング ダンプ ファイルを生成します。 + + + + Hang dump + ハング ダンプ + + + + Specify the name of the dump file + ダンプ ファイルの名前を指定する + + + + Generate a dump file if the test process hangs + テスト プロセスがハングした場合にダンプ ファイルを生成する + + + + Hang dump timeout of '{0}' expired + '{0}' のハング ダンプ タイムアウトの有効期限が切れました + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + ダンプが生成されるまでのタイムアウトを指定します。タイムアウト値は、次のいずれかの形式で指定されます 1.5h、1.5hour、1.5hours、90m、90min、90minute、90minutes、5400s、5400sec、5400second、5400seconds.。既定値は 30m です。 + + + + '--hangdump-timeout' expects a single timeout argument + '--hangdump-timeout' には 1 つのタイムアウト引数が必要です + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + ダンプの型を指定します。有効な値は、'Mini'、'Heap'、'Triage' (.NET 6 以降でのみ利用可能)、または 'Full' です。既定の型は 'Full' です + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + '{0}' は有効なダンプの種類ではありません。有効なオプションは、'Mini'、'Heap'、'Triage' (.NET 6 以降でのみ利用可能)、'Full' です + + + + Request of type '{0}' is not supported + 型 '{0}' の要求はサポートされていません + + + + The list of tests that were running at the time of the hang + ハング時に実行されていたテストの一覧 + + + + Hang test list + ハング テスト リスト + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + 1 つ以上のハング ダンプ パラメーターを指定しましたが、有効にしませんでした。コマンド ラインに --hangdump を追加してください + + + + Cannot find mutex '{0}' + ミューテックス '{0}' が見つかりません + + + + Mutex name wasn't received after '{0}' seconds + '{0}' 秒後にミューテックス名が受信されませんでした + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + ダンプの取得時に次のテストがまだ実行されていました (形式: [<time-elapsed-since-start>] <name>): + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ko.xlf new file mode 100644 index 0000000000..6180f67109 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ko.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + 덤프 파일 '{0}'을(를) 만드는 중 + + + + The hang dump file + 중단 덤프 파일 + + + + Hang dump file + 중단 덤프 파일 + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + 환경 변수 '{0}'이(가) '{2}' 대신 '{1}'로 설정되어 있습니다. + + + + Environment variable '{0}' is not set + 환경 변수 ‘{0}’이(가) 설정되지 않았습니다. + + + + Produce hang dump files when a test execution exceed a given time. + 테스트 실행이 지정된 시간을 초과하면 중단 덤프 파일을 생성합니다. + + + + Hang dump + 덤프 중단 + + + + Specify the name of the dump file + 덤프 파일의 이름 지정 + + + + Generate a dump file if the test process hangs + 테스트 프로세스가 중단되면 덤프 파일 생성 + + + + Hang dump timeout of '{0}' expired + '{0}'의 중단 덤프 시간 제한이 만료되었습니다. + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + 덤프가 생성되기 전까지의 시간 제한을 지정합니다. 시간 제한 값은 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds 형식 중 하나로 지정됩니다. 기본값은 30m입니다. + + + + '--hangdump-timeout' expects a single timeout argument + '--hangdump-timeout'에는 단일 시간 제한 인수가 필요합니다. + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + 덤프 유형을 지정하십시오. 유효한 값은 'Mini', 'Heap', 'Triage'(.NET 6 이상에서만 사용 가능) 또는 'Full'입니다. 기본 유형은 'Full'입니다. + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + '{0}'은(는) 올바른 덤프 유형이 아닙니다. 유효한 옵션은 'Mini', 'Heap', 'Triage'(.NET 6 이상에서만 사용 가능) 및 'Full'입니다. + + + + Request of type '{0}' is not supported + '{0}' 유형의 요청은 지원되지 않습니다. + + + + The list of tests that were running at the time of the hang + 중단 시 실행된 테스트 목록입니다. + + + + Hang test list + 테스트 목록 중단 + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + 하나 이상의 중단 덤프 매개 변수를 지정했지만 사용하도록 설정하지 않았습니다. 명령줄에 --hangdump를 추가하세요. + + + + Cannot find mutex '{0}' + 뮤텍스 '{0}'을(를) 찾을 수 없습니다. + + + + Mutex name wasn't received after '{0}' seconds + '{0}' 초 후에 뮤텍스 이름을 받지 못했습니다. + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + 덤프를 수행할 때 다음 테스트가 계속 실행되고 있었습니다(형식: [<time-elapsed-since-start>] <name>). + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.pl.xlf new file mode 100644 index 0000000000..3490170209 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.pl.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + Tworzenie pliku zrzutu „{0}” + + + + The hang dump file + Plik zrzutu zawieszenia + + + + Hang dump file + Zawieszanie pliku zrzutu + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + Zmienna środowiskowa „{0}” jest ustawiona na wartość „{1}” zamiast „{2}” + + + + Environment variable '{0}' is not set + Zmienna środowiskowa „{0}” nie jest ustawiona + + + + Produce hang dump files when a test execution exceed a given time. + Utwórz pliki zrzutu zawieszenia, gdy wykonanie testu przekroczy dany czas. + + + + Hang dump + Zrzut zawieszenia + + + + Specify the name of the dump file + Określ nazwę pliku zrzutu + + + + Generate a dump file if the test process hangs + Wygeneruj plik zrzutu, jeśli proces testowy zawiesza się + + + + Hang dump timeout of '{0}' expired + Upłynął limit czasu zrzutu zawieszenia „{0}” + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + Określ limit czasu, po którym zostanie wygenerowany zrzut. Wartość limitu czasu jest określona w jednym z następujących formatów: 1,5 godziny, 1,5 godziny, 90 m, 90 min, 90 minut, 90 minut 5400 s, 5400 s, 5400 sekund, 5400 sekund, 5400 sekund. Wartość domyślna to 30 m. + + + + '--hangdump-timeout' expects a single timeout argument + Element „--hangdump-timeout” oczekuje pojedynczego argumentu limitu czasu + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + Określ typ zrzutu. Prawidłowe wartości to "Mini", "Heap", "Triage" (dostępne tylko na platformie .NET 6+) lub „Pełne”. Domyślny typ to „Pełne” + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + Typ „{0}” nie jest prawidłowym typem zrzutu. Prawidłowe opcje to „Mini”, „Sterta", "Klasyfikacja" (dostępne tylko na platformie .NET 6+) i „Pełne” + + + + Request of type '{0}' is not supported + Żądanie typu „{0}” nie jest obsługiwane + + + + The list of tests that were running at the time of the hang + Lista testów, które były uruchamiane w czasie zawieszenia + + + + Hang test list + Lista testów zawieszenia + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + Określono co najmniej jeden parametr zrzutu zawieszenia, ale go nie włączono. Dodaj parametr --hangdump do wiersza polecenia + + + + Cannot find mutex '{0}' + Nie można odnaleźć obiektu mutex „{0}” + + + + Mutex name wasn't received after '{0}' seconds + Nazwa obiektu Mutex nie została odebrana po „{0}” s + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + Następujące testy były nadal uruchomione podczas wykonywania zrzutu (format: [<time-elapsed-since-start>] <name>): + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.pt-BR.xlf new file mode 100644 index 0000000000..8c09bfb0f4 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.pt-BR.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + Criando arquivo de despejo ''{0}'' + + + + The hang dump file + O arquivo de despejo de travamento + + + + Hang dump file + Arquivo de despejo de travamento + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + A variável de ambiente ''{0}'' ambiente está definida como ''{1}'' em vez de ''{2}'' + + + + Environment variable '{0}' is not set + A variável de ambiente ''{0}'' não está definida + + + + Produce hang dump files when a test execution exceed a given time. + Produzir arquivos de despejo de travamento quando uma execução de teste exceder um determinado tempo. + + + + Hang dump + Despejo de travamento + + + + Specify the name of the dump file + Especifique o nome do arquivo de despejo + + + + Generate a dump file if the test process hangs + Gerar um arquivo de despejo se o processo de teste travar + + + + Hang dump timeout of '{0}' expired + Tempo limite de despejo de travamento de ''{0}'' expirado + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + Especifique o tempo limite após o qual o despejo será gerado. O valor de tempo limite é especificado em um dos seguintes formatos: 1.5h, 1,5hora, 1,5horas, 90m, 90min, 90minuto, 90minutos 5400s, 5400seg, 5400segundo, 5400segundos. O padrão é 30m. + + + + '--hangdump-timeout' expects a single timeout argument + ''--hangdump-timeout'' espera um único argumento de tempo limite + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + Especifique o tipo de despejo. Os valores válidos são ''Mini'', ''Heap'', ''Triage'' (disponível somente no .NET 6+) e ''Full'' O tipo padrão é ''Full'' + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + ''{0}'' não é um tipo de despejo válido. As opções válidas são ''Mini'', ''Heap'', ''Triage'' (disponível somente no .NET 6+) e ''Full'' + + + + Request of type '{0}' is not supported + Não há suporte para a solicitação do tipo ''{0}'' + + + + The list of tests that were running at the time of the hang + A lista de testes que estavam sendo executados no momento do travamento + + + + Hang test list + Lista de testes de travamento + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + Você especificou um ou mais parâmetros de hang dump, mas não os ativou, adicione --hangdump à linha de comando + + + + Cannot find mutex '{0}' + Não é possível localizar o mutex ''{0}'' + + + + Mutex name wasn't received after '{0}' seconds + O nome do mutex não foi recebido após ''{0}'' segundos + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + Os testes a seguir ainda estavam em execução quando o despejo foi realizado (formato: [<time-elapsed-since-start>] <name>): + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ru.xlf new file mode 100644 index 0000000000..77323c6f4d --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.ru.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + Создание файла дампа "{0}" + + + + The hang dump file + Файл дампа зависания + + + + Hang dump file + Файл дампа зависания + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + Переменной среды "{0}" присвоено значение "{1}" вместо "{2}" + + + + Environment variable '{0}' is not set + Переменная среды "{0}" не задана + + + + Produce hang dump files when a test execution exceed a given time. + Создавать файлы дампа зависания при превышении заданного времени выполнения теста. + + + + Hang dump + Дамп зависания + + + + Specify the name of the dump file + Укажите имя файла дампа зависания + + + + Generate a dump file if the test process hangs + Создавать файл дампа, если тестовый процесс завис + + + + Hang dump timeout of '{0}' expired + Истекло время ожидания дампа зависания "{0}" + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + Укажите время ожидания, по истечении которого будет создан дамп. Значение времени ожидания указано в одном из следующих форматов: 1,5 ч, 1,5 часа, 90 м, 90 мин, 90 минут, 5400 с, 5400 сек, 5400 секунд. Значение по умолчанию — 30 м. + + + + '--hangdump-timeout' expects a single timeout argument + "--hangdump-timeout" ожидает один аргумент времени ожидания + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + Укажите тип дампа. Допустимые значения: "Mini", "Heap", "Triage" (доступно только в .NET 6+) или "Full". Тип по умолчанию — "Full" + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + "{0}" не является допустимым типом дампа. Допустимые параметры: "Mini", "Heap", "Triage" (доступно только в .NET 6+) и "Full" + + + + Request of type '{0}' is not supported + Запрос типа "{0}" не поддерживается + + + + The list of tests that were running at the time of the hang + Список тестов, которые были запущены во время зависания + + + + Hang test list + Список тестов зависаний + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + Вы указали один или несколько параметров дампа зависания, но не включили его. Добавьте --hangdump в командную строку + + + + Cannot find mutex '{0}' + Не удается найти мьютекс "{0}". + + + + Mutex name wasn't received after '{0}' seconds + Имя мьютекса не было получено через "{0}" секунд + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + При создании дампа все еще выполнялись следующие тесты (формат: [<time-elapsed-since-start>] <name>): + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.tr.xlf new file mode 100644 index 0000000000..0d24f38d3c --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.tr.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + Döküm dosyası '{0}' oluşturuluyor + + + + The hang dump file + Askıda kalma dökümü dosyası + + + + Hang dump file + Döküm dosyasını as + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + '{0}' ortam değişkeni '{1}' yerine '{2}' olarak ayarlandı + + + + Environment variable '{0}' is not set + '{0}' ortam değişkeni ayarlı değil + + + + Produce hang dump files when a test execution exceed a given time. + Bir test yürütmesi belirli bir süreyi aştığında askıda kalan döküm dosyaları üretin. + + + + Hang dump + Askı dökümü + + + + Specify the name of the dump file + Döküm dosyasının adını belirtin + + + + Generate a dump file if the test process hangs + Test işlemi kilitlenirse bir döküm dosyası oluşturun + + + + Hang dump timeout of '{0}' expired + '{0}' dökümünün askıda kalma zaman aşımı süresi doldu + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + Dökümün oluşturulacağı zaman aşımını belirtin. Zaman aşımı değeri şu formatlardan birinde belirtilir: 1,5 saat, 1,5 saat, 1,5 saat, 90 dakika, 90 dakika, 90 dakika, 90 dakika 5400s, 5400sn, 5400saniye, 5400saniye. Varsayılan değer 30m. + + + + '--hangdump-timeout' expects a single timeout argument + '--hangdump-timeout' tek bir zaman aşımı bağımsız değişkeni bekliyor + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + Dökümün türünü belirtin. Geçerli değerler 'Mini', 'Heap', 'Triage' (yalnızca .NET 6+'da mevcuttur) veya 'Full'dur. Varsayılan tür 'Tam'dır + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + '{0}' geçerli bir döküm türü değil. Geçerli seçenekler 'Mini', 'Heap', 'Triage' (yalnızca .NET 6+'da mevcuttur) ve 'Full’dur + + + + Request of type '{0}' is not supported + '{0}' türündeki istek desteklenmiyor + + + + The list of tests that were running at the time of the hang + Askıya alınma anında çalışan testlerin listesi + + + + Hang test list + Askıda kalma test listesi + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + Bir veya daha fazla askıda kalma dökümü parametresi belirtmenize rağmen bunu etkinleştirmediyseniz komut satırına --hangdump ekleyin + + + + Cannot find mutex '{0}' + Muteks '{0}' bulunamıyor + + + + Mutex name wasn't received after '{0}' seconds + Mutex adı '{0}' saniyeden sonra alınamadı + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + Döküm alındığında aşağıdaki testler hâlâ çalışıyordu (biçim: [<time-elapsed-since-start>] <name>): + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.zh-Hans.xlf new file mode 100644 index 0000000000..451533eabd --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.zh-Hans.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + 正在创建转储文件“{0}” + + + + The hang dump file + 挂起转储文件 + + + + Hang dump file + 挂起转储文件 + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + 环境变量“{0}”设置为“{1}”,而不是“{2}” + + + + Environment variable '{0}' is not set + 未设置环境变量“{0}” + + + + Produce hang dump files when a test execution exceed a given time. + 当测试执行超过给定时间时生成挂起转储文件。 + + + + Hang dump + 挂起转储 + + + + Specify the name of the dump file + 指定转储文件的名称 + + + + Generate a dump file if the test process hangs + 如果测试进程挂起,则生成转储文件 + + + + Hang dump timeout of '{0}' expired + 挂起转储超时“{0}”已过期 + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + 指定生成转储后的超时。超时值以以下格式之一指定: 1.5h、1.5hour、1.5hours、90m、90min、90minute、90minutes 5400s、5400sec、5400second、5400seconds。默认值为 30m。 + + + + '--hangdump-timeout' expects a single timeout argument + “--hangdump-timeout”需要单一超时参数 + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + 指定转储的类型。有效值为“Mini”、“Heap”、“Triage”(仅适用于 .NET 6+)或“Full”。默认类型为“Full” + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + “{0}”不是有效的转储类型。有效选项为“Mini”、“Heap”、“Triage”(仅适用于 .NET 6+)和“Full” + + + + Request of type '{0}' is not supported + 不支持类型为“{0}”的请求 + + + + The list of tests that were running at the time of the hang + 挂起时运行的测试列表 + + + + Hang test list + 挂起测试列表 + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + 你指定了一个或多个挂起转储参数,但未启用它,请将 --hangdump 添加到命令行 + + + + Cannot find mutex '{0}' + 找不到互斥“{0}” + + + + Mutex name wasn't received after '{0}' seconds + “{0}”秒后未收到互斥名称 + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + 执行转储时,以下测试仍在运行(格式: [<time-elapsed-since-start>] <name>): + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.zh-Hant.xlf new file mode 100644 index 0000000000..e3b064eab3 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Resources/xlf/ExtensionResources.zh-Hant.xlf @@ -0,0 +1,112 @@ + + + + + + Creating dump file '{0}' + 正在建立傾印檔案 '{0}' + + + + The hang dump file + 擱置傾印檔案 + + + + Hang dump file + 擱置傾印檔案 + + + + Environment variable '{0}' is set to '{1}' instead of '{2}' + 環境變數 '{0}' 已設定為 '{1}' 而不是 '{2}' + + + + Environment variable '{0}' is not set + 未設定環境變數 '{0}' + + + + Produce hang dump files when a test execution exceed a given time. + 當測試執行超過指定時間時,產生擱置傾印檔案。 + + + + Hang dump + 擱置傾印 + + + + Specify the name of the dump file + 指定傾印檔案的名稱 + + + + Generate a dump file if the test process hangs + 如果測試程序擱置,則產生傾印檔案 + + + + Hang dump timeout of '{0}' expired + '{0}' 的擱置傾印逾時已過期 + + + + Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + 指定產生傾印前的逾時時間。逾時值會以下列其中一種格式顯示: 1.5h、1.5hour、1.5hours、90m、90min、90minute、90minutes 5400s、5400sec、5400second、5400seconds。預設為 30m。 + + + + '--hangdump-timeout' expects a single timeout argument + '--hangdump-timeout' 需要單一逾時引數 + + + + Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + 指定傾印的類型。有效值為 'Mini'、'Heap'、'Triage' (只有在 .NET 6+ 可供使用) 或 'Full'。預設類型為 'Full' + + + + '{0}' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full' + '{0}' 不是有效的傾印類型。有效的選項為 'Mini'、'Heap'、'Triage' (只有在 .NET 6+ 可供使用) 和 'Full' + + + + Request of type '{0}' is not supported + 不支援類型 '{0}' 的要求 + + + + The list of tests that were running at the time of the hang + 擱置時正在執行的測試清單 + + + + Hang test list + 擱置測試清單 + + + + You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line + 您指定了一或多個停留傾印參數但並未加以啟用,請將 --hangdump 新增至命令列中 + + + + Cannot find mutex '{0}' + 找不到 Mutex '{0}' + + + + Mutex name wasn't received after '{0}' seconds + '{0}' 秒後未收到 Mutex 名稱 + + + + The following tests were still running when dump was taken (format: [<time-elapsed-since-start>] <name>): + 執行傾印時,下列測試仍在執行 (格式: [<time-elapsed-since-start>] <name>): + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ActivityIndicatorMutexNameSerializer.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ActivityIndicatorMutexNameSerializer.cs new file mode 100644 index 0000000000..c46d16b03c --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ActivityIndicatorMutexNameSerializer.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Extensions.HangDump.Serializers; + +internal sealed class ActivityIndicatorMutexNameRequest(string mutexName) : IRequest +{ + public string MutexName { get; } = mutexName; +} + +internal sealed class ActivityIndicatorMutexNameRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 1; + + public object Deserialize(Stream stream) + { + string mutexName = ReadString(stream); + return new ActivityIndicatorMutexNameRequest(mutexName); + } + + public void Serialize(object objectToSerialize, Stream stream) + { + var request = (ActivityIndicatorMutexNameRequest)objectToSerialize; + WriteString(stream, request.MutexName); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ConsumerPipeNameSerializer.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ConsumerPipeNameSerializer.cs new file mode 100644 index 0000000000..a2f5949803 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ConsumerPipeNameSerializer.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Extensions.HangDump.Serializers; + +internal sealed class ConsumerPipeNameRequest(string pipeName) : IRequest +{ + public string PipeName { get; } = pipeName; +} + +internal sealed class ConsumerPipeNameRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 3; + + public object Deserialize(Stream stream) + { + string mutexName = ReadString(stream); + return new ConsumerPipeNameRequest(mutexName); + } + + public void Serialize(object objectToSerialize, Stream stream) + { + var request = (ConsumerPipeNameRequest)objectToSerialize; + WriteString(stream, request.PipeName); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ExitSignalActivityIndicatorTaskRequest.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ExitSignalActivityIndicatorTaskRequest.cs new file mode 100644 index 0000000000..a1f76cf3b9 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/ExitSignalActivityIndicatorTaskRequest.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Extensions.HangDump.Serializers; + +internal sealed class ExitSignalActivityIndicatorTaskRequest() : IRequest; + +internal sealed class ExitSignalActivityIndicatorTaskRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 6; + + public object Deserialize(Stream stream) => new ExitSignalActivityIndicatorTaskRequest(); + + public void Serialize(object objectToSerialize, Stream stream) + { + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsRequest.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsRequest.cs new file mode 100644 index 0000000000..2a68a98905 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsRequest.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Extensions.HangDump.Serializers; + +internal sealed class GetInProgressTestsRequest() : IRequest; + +internal sealed class GetInProgressTestsRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 4; + + public object Deserialize(Stream stream) => new GetInProgressTestsRequest(); + + public void Serialize(object objectToSerialize, Stream stream) + { + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsResponse.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsResponse.cs new file mode 100644 index 0000000000..d393963ca6 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/GetInProgressTestsResponse.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Extensions.HangDump.Serializers; + +internal sealed class GetInProgressTestsResponse((string, int)[] tests) : IResponse +{ + public (string, int)[] Tests { get; } = tests; +} + +internal sealed class GetInProgressTestsResponseSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 5; + + public object Deserialize(Stream stream) + { + int readCount = ReadInt(stream); + List<(string, int)> tests = new(); + for (int i = 0; i < readCount; i++) + { + string testName = ReadString(stream); + int unixTimeSeconds = ReadInt(stream); + tests.Add((testName, unixTimeSeconds)); + } + + return new GetInProgressTestsResponse(tests.ToArray()); + } + + public void Serialize(object objectToSerialize, Stream stream) + { + var getInProgressTestsResponse = (GetInProgressTestsResponse)objectToSerialize; + WriteInt(stream, getInProgressTestsResponse.Tests.Length); + foreach ((string testName, int seconds) in getInProgressTestsResponse.Tests) + { + WriteString(stream, testName); + WriteInt(stream, seconds); + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/SessionEndSerializer.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/SessionEndSerializer.cs new file mode 100644 index 0000000000..23cd132c19 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Serializers/SessionEndSerializer.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Extensions.HangDump.Serializers; + +internal sealed class SessionEndSerializerRequest() : IRequest; + +internal sealed class SessionEndSerializerRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 2; + + public object Deserialize(Stream stream) + => new SessionEndSerializerRequest(); + + public void Serialize(object _, Stream __) + { + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/TestingPlatformBuilderHook.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/TestingPlatformBuilderHook.cs new file mode 100644 index 0000000000..9b5089054a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/TestingPlatformBuilderHook.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Builder; + +namespace Microsoft.Testing.Extensions.HangDump; + +public static class TestingPlatformBuilderHook +{ + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _) + => testApplicationBuilder.AddHangDumpProvider(); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/WindowsMiniDumpWriteDump.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/WindowsMiniDumpWriteDump.cs new file mode 100644 index 0000000000..7217148459 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/WindowsMiniDumpWriteDump.cs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +using Microsoft.Win32.SafeHandles; + +namespace Microsoft.Testing.Extensions.Diagnostics; + +internal static class MiniDumpWriteDump +{ + public static void CollectDumpUsingMiniDumpWriteDump(int pid, string outputFile, MiniDumpTypeOption type) + { + var process = Process.GetProcessById(pid); + + // Open the file for writing + using var stream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite, FileShare.None); + NativeMethods.MinidumpExceptionInformation exceptionInfo = default; + + NativeMethods.MinidumpType dumpType = type switch + { + MiniDumpTypeOption.Full => + NativeMethods.MinidumpType.MiniDumpWithFullMemory + | NativeMethods.MinidumpType.MiniDumpWithDataSegs + | NativeMethods.MinidumpType.MiniDumpWithHandleData + | NativeMethods.MinidumpType.MiniDumpWithUnloadedModules + | NativeMethods.MinidumpType.MiniDumpWithFullMemoryInfo + | NativeMethods.MinidumpType.MiniDumpWithThreadInfo + | NativeMethods.MinidumpType.MiniDumpWithTokenInformation, + MiniDumpTypeOption.Heap => + NativeMethods.MinidumpType.MiniDumpWithPrivateReadWriteMemory + | NativeMethods.MinidumpType.MiniDumpWithDataSegs + | NativeMethods.MinidumpType.MiniDumpWithHandleData + | NativeMethods.MinidumpType.MiniDumpWithUnloadedModules + | NativeMethods.MinidumpType.MiniDumpWithFullMemoryInfo + | NativeMethods.MinidumpType.MiniDumpWithThreadInfo + | NativeMethods.MinidumpType.MiniDumpWithTokenInformation, + MiniDumpTypeOption.Mini => NativeMethods.MinidumpType.MiniDumpWithThreadInfo, + _ => NativeMethods.MinidumpType.MiniDumpNormal, + }; + + // Retry the write dump on ERROR_PARTIAL_COPY + for (int i = 0; i < 5; i++) + { + // Dump the process! + if (NativeMethods.MiniDumpWriteDump(process.Handle, (uint)process.Id, stream.SafeFileHandle, dumpType, ref exceptionInfo, IntPtr.Zero, IntPtr.Zero)) + { + break; + } + + int err = Marshal.GetHRForLastWin32Error(); + if (err != NativeMethods.ErrorPartialCopy) + { + Marshal.ThrowExceptionForHR(err); + } + } + } + + private static class NativeMethods + { + public const int ErrorPartialCopy = unchecked((int)0x8007012b); + + [DllImport("Dbghelp.dll", SetLastError = true)] + public static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeFileHandle hFile, MinidumpType dumpType, ref MinidumpExceptionInformation exceptionParam, IntPtr userStreamParam, IntPtr callbackParam); + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public readonly struct MinidumpExceptionInformation + { + public readonly uint ThreadId; + public readonly IntPtr ExceptionPointers; + public readonly int ClientPointers; + } + + [Flags] + public enum MinidumpType : uint + { + MiniDumpNormal = 0, + MiniDumpWithDataSegs = 1 << 0, + MiniDumpWithFullMemory = 1 << 1, + MiniDumpWithHandleData = 1 << 2, + MiniDumpFilterMemory = 1 << 3, + MiniDumpScanMemory = 1 << 4, + MiniDumpWithUnloadedModules = 1 << 5, + MiniDumpWithIndirectlyReferencedMemory = 1 << 6, + MiniDumpFilterModulePaths = 1 << 7, + MiniDumpWithProcessThreadData = 1 << 8, + MiniDumpWithPrivateReadWriteMemory = 1 << 9, + MiniDumpWithoutOptionalData = 1 << 10, + MiniDumpWithFullMemoryInfo = 1 << 11, + MiniDumpWithThreadInfo = 1 << 12, + MiniDumpWithCodeSegs = 1 << 13, + MiniDumpWithoutAuxiliaryState = 1 << 14, + MiniDumpWithFullAuxiliaryState = 1 << 15, + MiniDumpWithPrivateWriteCopyMemory = 1 << 16, + MiniDumpIgnoreInaccessibleMemory = 1 << 17, + MiniDumpWithTokenInformation = 1 << 18, + MiniDumpWithModuleHeaders = 1 << 19, + MiniDumpFilterTriage = 1 << 20, + MiniDumpWithAvxXStateContext = 1 << 21, + MiniDumpWithIptTrace = 1 << 22, + MiniDumpValidTypeFlags = (-1) ^ ((~1) << 22), + } + } + + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "Copy-paste")] + internal enum MiniDumpTypeOption + { + // This is a copy of DumpTypeOption, we need both of those types, because this + // dumper is included as file in both MiniDumpTool, and BlameDataCollector, and + // blame references MiniDumpTool, so we get the enum defined twice otherwise. + Full, + Heap, + Mini, + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/build/Microsoft.Testing.Extensions.HangDump.props b/src/Platform/Microsoft.Testing.Extensions.HangDump/build/Microsoft.Testing.Extensions.HangDump.props new file mode 100644 index 0000000000..fb4e05c801 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/build/Microsoft.Testing.Extensions.HangDump.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/buildMultiTargeting/Microsoft.Testing.Extensions.HangDump.props b/src/Platform/Microsoft.Testing.Extensions.HangDump/buildMultiTargeting/Microsoft.Testing.Extensions.HangDump.props new file mode 100644 index 0000000000..400685711e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/buildMultiTargeting/Microsoft.Testing.Extensions.HangDump.props @@ -0,0 +1,9 @@ + + + + + Microsoft.Testing.Extensions.HangDump + Microsoft.Testing.Extensions.HangDump.TestingPlatformBuilderHook + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/buildTransitive/Microsoft.Testing.Extensions.HangDump.props b/src/Platform/Microsoft.Testing.Extensions.HangDump/buildTransitive/Microsoft.Testing.Extensions.HangDump.props new file mode 100644 index 0000000000..fb4e05c801 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/buildTransitive/Microsoft.Testing.Extensions.HangDump.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClient.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClient.cs new file mode 100644 index 0000000000..9fa6fa9296 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClient.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.ApplicationInsights; +using Microsoft.ApplicationInsights.Extensibility; + +namespace Microsoft.Testing.Extensions.Telemetry; + +internal class AppInsightTelemetryClient : ITelemetryClient +{ + // Note: The InstrumentationKey should match the one of dotnet cli. + private const string InstrumentationKey = "74cc1c9e-3e6e-4d05-b3fc-dde9101d0254"; + private const string TelemetryServiceEndpoint = "https://dc.services.visualstudio.com/"; + + private readonly TelemetryConfiguration _config; + private readonly TelemetryClient _telemetryClient; + + public AppInsightTelemetryClient(string? currentSessionId, string osVersion) + { + _config = TelemetryConfiguration.CreateDefault(); + _config.ConnectionString = $"InstrumentationKey={InstrumentationKey};IngestionEndpoint={TelemetryServiceEndpoint}"; + _telemetryClient = new TelemetryClient(_config); + _telemetryClient.Context.Session.Id = currentSessionId; + _telemetryClient.Context.Device.OperatingSystem = osVersion; + } + + public void TrackEvent(string eventName, Dictionary properties, Dictionary metrics) + { + _telemetryClient.TrackEvent(eventName, properties, metrics); + _telemetryClient.Flush(); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClientFactory.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClientFactory.cs new file mode 100644 index 0000000000..8e3665349c --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClientFactory.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Extensions.Telemetry; + +internal class AppInsightTelemetryClientFactory : ITelemetryClientFactory +{ + public ITelemetryClient Create(string? currentSessionId, string osVersion) + => new AppInsightTelemetryClient(currentSessionId, osVersion); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsProvider.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsProvider.cs new file mode 100644 index 0000000000..99d8e15d4a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsProvider.cs @@ -0,0 +1,336 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if NETCOREAPP +using System.Threading.Channels; +#else +using System.Collections.Concurrent; +#endif +using System.Globalization; +using System.Text; + +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Services; +using Microsoft.Testing.Platform.Telemetry; + +namespace Microsoft.Testing.Extensions.Telemetry; + +/// +/// Allows to log telemetry events via AppInsights. +/// +internal sealed partial class AppInsightsProvider : + ITelemetryCollector +#pragma warning disable SA1001 // Commas should be spaced correctly +#if NETCOREAPP + , IAsyncDisposable +#else + , IDisposable +#endif +#pragma warning restore SA1001 // Commas should be spaced correctly +{ + // Note: We're currently using the same environment variable as dotnet CLI. + public static readonly string SessionIdEnvVar = "TESTINGPLATFORM_APPINSIGHTS_SESSIONID"; + + // Allows us to correlate events produced from the same process. + // Not calling this ProcessId, because it has a different meaning. + private static readonly string CurrentReporterId = Guid.NewGuid().ToString(); + private readonly string _currentSessionId; + private readonly bool _isCi; + private readonly IEnvironment _environment; + private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource; + private readonly ITask _task; + private readonly IClock _clock; + private readonly ITelemetryInformation _telemetryInformation; + private readonly ITelemetryClientFactory _telemetryClientFactory; + private readonly bool _isDevelopmentRepository; + private readonly ILogger _logger; + private readonly Task? _telemetryTask; + private readonly CancellationTokenSource _flushTimeoutOrStop = new(); +#if NETCOREAPP + private readonly Channel<(string EventName, IDictionary ParamsMap)> _payloads; +#else + private readonly BlockingCollection<(string EventName, IDictionary ParamsMap)> _payloads; +#endif +#if DEBUG + // Telemetry properties that are allowed to contain unhashed information. + private static readonly HashSet StringWhitelist = + [ + TelemetryProperties.VersionPropertyName, + TelemetryProperties.ReporterIdPropertyName, + TelemetryProperties.SessionId, + TelemetryProperties.HostProperties.TestingPlatformVersionPropertyName, + TelemetryProperties.HostProperties.FrameworkDescriptionPropertyName, + TelemetryProperties.HostProperties.OSDescriptionPropertyName, + TelemetryProperties.HostProperties.RuntimeIdentifierPropertyName, + TelemetryProperties.HostProperties.ApplicationModePropertyName, + TelemetryProperties.HostProperties.ExitCodePropertyName, + TelemetryProperties.HostProperties.ExtensionsPropertyName + ]; +#endif + + private ITelemetryClient? _client; + private bool _isDisposed; + + public AppInsightsProvider( + IEnvironment environment, + ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource, + ITask task, + ILoggerFactory loggerFactory, + IClock clock, + IConfiguration configuration, + ITelemetryInformation telemetryInformation, + ITelemetryClientFactory telemetryClientFactory, + string sessionId) + { + _ = bool.TryParse(configuration[PlatformConfigurationConstants.PlatformTelemetryIsDevelopmentRepository], out _isDevelopmentRepository); + _isCi = CIEnvironmentDetectorForTelemetry.IsCIEnvironment(); + _environment = environment; + _currentSessionId = sessionId; + _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; + _task = task; + _clock = clock; + _telemetryInformation = telemetryInformation; + _telemetryClientFactory = telemetryClientFactory; + +#if NETCOREAPP + _payloads = Channel.CreateUnbounded<(string EventName, IDictionary ParamsMap)>(new UnboundedChannelOptions() + { + // We process only 1 data at a time + SingleReader = true, + + // We don't know how many threads will call the Log method + SingleWriter = false, + + // We want to unlink the caller from the consumer + AllowSynchronousContinuations = false, + }); + + _telemetryTask = task.Run(IngestLoopAsync, _testApplicationCancellationTokenSource.CancellationToken); +#else + // Keep the custom thread to avoid to waste one from thread pool. + // We have some await but we should stay on custom one if not for special needs like trace log or exception. + _payloads = new(); + _telemetryTask = _task.RunLongRunning(IngestLoopAsync, "Telemetry AppInsightsProvider", _testApplicationCancellationTokenSource.CancellationToken); +#endif + + _logger = loggerFactory.CreateLogger(); + } + + // Initialize the telemetry client and start ingesting events. + private async Task IngestLoopAsync() + { + if (_testApplicationCancellationTokenSource.CancellationToken.IsCancellationRequested) + { + return; + } + + try + { + _client = _telemetryClientFactory.Create(_currentSessionId, _environment.OsVersion); + } + catch (Exception e) + { + _client = null; + + await _logger.LogErrorAsync($"Failed to initialize telemetry client", e); + return; + } + + DateTimeOffset? lastLoggedError = null; + _testApplicationCancellationTokenSource.CancellationToken.Register(() => _flushTimeoutOrStop.Cancel()); + try + { +#if NETCOREAPP + while (await _payloads.Reader.WaitToReadAsync(_flushTimeoutOrStop.Token)) + { + (string eventName, IDictionary paramsMap) = await _payloads.Reader.ReadAsync(); +#else + foreach ((string eventName, IDictionary paramsMap) in _payloads.GetConsumingEnumerable(_flushTimeoutOrStop.Token)) + { +#endif + + // Add common properties. + paramsMap.Add(TelemetryProperties.VersionPropertyName, _telemetryInformation.Version); + paramsMap.Add(TelemetryProperties.SessionId, _currentSessionId); + paramsMap.Add(TelemetryProperties.ReporterIdPropertyName, CurrentReporterId); + paramsMap.Add(TelemetryProperties.IsCIPropertyName, _isCi.AsTelemetryBool()); + + if (_isDevelopmentRepository) + { + paramsMap.Add(TelemetryProperties.HostProperties.IsDevelopmentRepositoryPropertyName, TelemetryProperties.True); + } + + var metrics = new Dictionary(); + var properties = new Dictionary(); + + foreach (KeyValuePair pair in paramsMap) + { + switch (pair.Value) + { + // Metrics: + case double value: + metrics.Add(pair.Key, value); + break; + case DateTimeOffset value: + metrics.Add(pair.Key, ToUnixTimeNanoseconds(value)); + break; + + // Properties: +#if DEBUG + case string value: + AssertHashed(pair.Key, value); + properties.Add(pair.Key, value); + break; +#endif + case bool value: + RoslynDebug.Assert(false, $"Telemetry entry '{pair.Key}' contains a boolean value, boolean values should always be converted to string using: .{nameof(TelemetryExtensions.AsTelemetryBool)}()"); + properties.Add(pair.Key, value.AsTelemetryBool()); + break; + default: + properties.Add(pair.Key, pair.Value?.ToString() ?? string.Empty); + break; + } + } + + if (_logger.IsEnabled(LogLevel.Trace)) + { + StringBuilder builder = new(); + builder.AppendLine(CultureInfo.InvariantCulture, $"Send telemetry event: {eventName}"); + foreach (KeyValuePair keyValue in properties) + { + builder.AppendLine(CultureInfo.InvariantCulture, $" {keyValue.Key}: {keyValue.Value}"); + } + + foreach (KeyValuePair keyValue in metrics) + { + builder.AppendLine(CultureInfo.InvariantCulture, $" {keyValue.Key}: {keyValue.Value.ToString("f", CultureInfo.InvariantCulture)}"); + } + + await _logger.LogTraceAsync(builder.ToString()); + } + + try + { + _client.TrackEvent(eventName, properties, metrics); + } + catch (Exception ex) + { + // If we have a lot of issues with the network we could have a lot of logs here. + // We log one error every 3 seconds. + // We could do better backpressure. + if (_logger.IsEnabled(LogLevel.Error) && (!lastLoggedError.HasValue || (lastLoggedError.Value - _clock.UtcNow).TotalSeconds > 3)) + { + await _logger.LogErrorAsync($"Error during telemetry report.", ex); + lastLoggedError = _clock.UtcNow; + } + } + } + } + catch (OperationCanceledException) + { + // This is expected when the test application is shutting down or if flush timeout. + return; + } + } + + private static double ToUnixTimeNanoseconds(DateTimeOffset value) => + + // The magic number is DateTimeOffset.UnixEpoch.Ticks in newer TFMs. + // We multiply by 100 because Ticks are 100 ns, and we want to report ns. + (value.UtcTicks - 621355968000000000L) * 100; + +#if DEBUG + private static void AssertHashed(string key, string value) + { + if (value is TelemetryProperties.True or TelemetryProperties.False) + { + return; + } + + // Full qualification of Regex to avoid adding conditional 'using' on top of the file. + if (value.Length == 64 && GetValidHashPattern().IsMatch(value)) + { + return; + } + + if (StringWhitelist.Contains(key)) + { + return; + } + + RoslynDebug.Assert(false, $"Telemetry entry '{key}' contains an unhashed string value '{value}'. Strings need to be hashed using {nameof(Sha256Hasher)}.{nameof(Sha256Hasher.HashWithNormalizedCasing)}(), or whitelisted."); + } + +#if NET7_0_OR_GREATER + [System.Text.RegularExpressions.GeneratedRegex("[a-f0-9]{64}")] + private static partial System.Text.RegularExpressions.Regex GetValidHashPattern(); +#else + private static System.Text.RegularExpressions.Regex GetValidHashPattern() + => new("[a-f0-9]{64}"); +#endif +#endif + + public async Task LogEventAsync(string eventName, IDictionary paramsMap) + { +#if NETCOREAPP + await _payloads.Writer.WriteAsync((eventName, paramsMap)); +#else + _payloads.Add((eventName, paramsMap)); + await Task.CompletedTask; +#endif + } + +#if !NETCOREAPP + // Adding dispose on graceful shutdown per https://github.com/microsoft/ApplicationInsights-dotnet/issues/1152#issuecomment-518742922 + public void Dispose() + { + _payloads.CompleteAdding(); + if (!_isDisposed) + { + if (_telemetryTask is null) + { + throw new InvalidOperationException("Unexpected null _telemetryTask"); + } + + int flushForSeconds = 3; + if (!_telemetryTask.Wait(TimeSpan.FromSeconds(flushForSeconds))) + { + _flushTimeoutOrStop.Cancel(); + _logger.LogWarning($"Telemetry task didn't flush after '{flushForSeconds}', some payload could be lost"); + } + + _isDisposed = true; + } + } +#endif + +#if NETCOREAPP + public async ValueTask DisposeAsync() + { + _payloads.Writer.Complete(); + if (!_isDisposed) + { + if (_telemetryTask is null) + { + throw new InvalidOperationException("Unexpected null _telemetryTask"); + } + + int flushForSeconds = 3; + try + { + await _telemetryTask.TimeoutAfterAsync(TimeSpan.FromSeconds(flushForSeconds)); + } + catch (TimeoutException) + { + await _flushTimeoutOrStop.CancelAsync(); + await _logger.LogWarningAsync($"Telemetry task didn't flush after '{flushForSeconds}', some payload could be lost"); + } + + _isDisposed = true; + } + } +#endif +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsTelemetryProviderExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsTelemetryProviderExtensions.cs new file mode 100644 index 0000000000..29f0e0ea24 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsTelemetryProviderExtensions.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.Telemetry; +using Microsoft.Testing.Extensions.Telemetry.Resources; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Services; +using Microsoft.Testing.Platform.Telemetry; + +#if NETCOREAPP +using System.Runtime.CompilerServices; +#endif + +namespace Microsoft.Testing.Extensions; + +public static class AppInsightsTelemetryProviderExtensions +{ + public static void AddAppInsightsTelemetryProvider(this ITestApplicationBuilder builder) + { +#if NETCOREAPP + // AppInsights is not supported on platforms that do not support dynamic code generation. + if (!RuntimeFeature.IsDynamicCodeSupported) + { + return; + } +#endif +#pragma warning disable IDE0022 // Use expression body for method + + if (builder is not TestApplicationBuilder testApplicationBuilder) + { + throw new ArgumentException(ExtensionResources.AddAppInsightsTelemetryProviderInvalidBuilder); + } + + testApplicationBuilder.TelemetryManager.AddTelemetryCollectorProvider(services => + { + IEnvironment environment = services.GetRequiredService(); + + // Session ID that is inherited across processes. + string sessionId = environment.GetEnvironmentVariable(AppInsightsProvider.SessionIdEnvVar) ?? Guid.NewGuid().ToString(); + + // We want to flow down the processes the same session id for correlation purposes. + environment.SetEnvironmentVariable(AppInsightsProvider.SessionIdEnvVar, sessionId); + return new AppInsightsProvider( + services.GetRequiredService(), + services.GetTestApplicationCancellationTokenSource(), + services.GetTask(), + services.GetLoggerFactory(), + services.GetClock(), + services.GetConfiguration(), + services.GetRequiredService(), + new AppInsightTelemetryClientFactory(), + sessionId); + }); +#pragma warning restore IDE0022 // Use expression body for method + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.Telemetry/BannedSymbols.txt new file mode 100644 index 0000000000..469bd16cf9 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/BannedSymbols.txt @@ -0,0 +1,4 @@ +M:System.String.IsNullOrEmpty(System.String); Use 'TAString.IsNullOrEmpty' instead +M:System.String.IsNullOrWhiteSpace(System.String); Use 'TAString.IsNullOrWhiteSpace' instead +M:System.Diagnostics.Debug.Assert(System.Boolean); Use 'RoslynDebug.Assert' instead +M:System.Diagnostics.Debug.Assert(System.Boolean,System.String); Use 'RoslynDebug.Assert' instead diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/CIEnvironmentDetectorForTelemetry.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/CIEnvironmentDetectorForTelemetry.cs new file mode 100644 index 0000000000..ee4c5a7f2e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/CIEnvironmentDetectorForTelemetry.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform; + +namespace Microsoft.Testing.Extensions.Telemetry; + +// Detection of CI: https://learn.microsoft.com/dotnet/core/tools/telemetry#continuous-integration-detection +// From: https://github.com/dotnet/sdk/blob/main/src/Cli/dotnet/Telemetry/CIEnvironmentDetectorForTelemetry.cs +internal class CIEnvironmentDetectorForTelemetry +{ + // Systems that provide boolean values only, so we can simply parse and check for true + private static readonly string[] BooleanVariables = + [ + // Azure Pipelines - https://docs.microsoft.com/azure/devops/pipelines/build/variables#system-variables-devops-services + "TF_BUILD", + + // GitHub Actions - https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables + "GITHUB_ACTIONS", + + // AppVeyor - https://www.appveyor.com/docs/environment-variables/ + "APPVEYOR", + + // A general-use flag - Many of the major players support this: AzDo, GitHub, GitLab, AppVeyor, Travis CI, CircleCI. + // Given this, we could potentially remove all of these other options? + "CI", + + // Travis CI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables + "TRAVIS", + + // CircleCI - https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables + "CIRCLECI" + ]; + + // Systems where every variable must be present and not-null before returning true + private static readonly string[][] AllNotNullVariables = new string[][] + { + // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html + ["CODEBUILD_BUILD_ID", "AWS_REGION"], + + // Jenkins - https://github.com/jenkinsci/jenkins/blob/master/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.groovy + ["BUILD_ID", "BUILD_URL"], + + // Google Cloud Build - https://cloud.google.com/build/docs/configuring-builds/substitute-variable-values#using_default_substitutions + ["BUILD_ID", "PROJECT_ID"], + }; + + // Systems where the variable must be present and not-null + private static readonly string[] IfNonNullVariables = + [ + // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html#Predefined+Server+Build+Parameters + "TEAMCITY_VERSION", + + // JetBrains Space - https://www.jetbrains.com/help/space/automation-environment-variables.html#general + "JB_SPACE_API_URL" + ]; + + public static bool IsCIEnvironment() + { + foreach (string booleanVariable in BooleanVariables) + { + if (bool.TryParse(Environment.GetEnvironmentVariable(booleanVariable), out bool envVar) && envVar) + { + return true; + } + } + + foreach (string[] variables in AllNotNullVariables) + { + if (variables.All((variable) => !RoslynString.IsNullOrEmpty(Environment.GetEnvironmentVariable(variable)))) + { + return true; + } + } + + foreach (string variable in IfNonNullVariables) + { + if (!RoslynString.IsNullOrEmpty(Environment.GetEnvironmentVariable(variable))) + { + return true; + } + } + + return false; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/ITelemetryClient.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/ITelemetryClient.cs new file mode 100644 index 0000000000..4cb3c606c0 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/ITelemetryClient.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Extensions.Telemetry; + +internal interface ITelemetryClient +{ + void TrackEvent(string eventName, Dictionary properties, Dictionary metrics); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/ITelemetryClientFactory.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/ITelemetryClientFactory.cs new file mode 100644 index 0000000000..b7651bdfad --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/ITelemetryClientFactory.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Extensions.Telemetry; + +internal interface ITelemetryClientFactory +{ + ITelemetryClient Create(string? currentSessionId, string osVersion); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Microsoft.Testing.Extensions.Telemetry.csproj b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Microsoft.Testing.Extensions.Telemetry.csproj new file mode 100644 index 0000000000..30c5d20049 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Microsoft.Testing.Extensions.Telemetry.csproj @@ -0,0 +1,66 @@ + + + + netstandard2.0;$(MicrosoftTestingTargetFrameworks) + + + + + + + + + + + + + + + + + + + + + + + + + + true + buildMultiTargeting + + + buildTransitive/$(TargetFramework) + + + build/$(TargetFramework) + + + + + + + + + + + + + + True + True + ExtensionResources.resx + + + + + + ResXFileCodeGenerator + ExtensionResources.Designer.cs + + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/PACKAGE.md b/src/Platform/Microsoft.Testing.Extensions.Telemetry/PACKAGE.md new file mode 100644 index 0000000000..46232617ad --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/PACKAGE.md @@ -0,0 +1,9 @@ +# Microsoft.Testing + +Microsoft Testing is a set of platform, framework and protocol intended to make it possible to run any test on any target or device. + +Documentation can be found at . + +## About + +This package provides telemetry extensions to Microsoft Testing Platform. diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.Telemetry/PublicAPI/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..fd2242e298 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/PublicAPI/PublicAPI.Shipped.txt @@ -0,0 +1,5 @@ +#nullable enable +Microsoft.Testing.Extensions.AppInsightsTelemetryProviderExtensions +Microsoft.Testing.Extensions.Telemetry.TestingPlatformBuilderHook +static Microsoft.Testing.Extensions.AppInsightsTelemetryProviderExtensions.AddAppInsightsTelemetryProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder) -> void +static Microsoft.Testing.Extensions.Telemetry.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! _) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.Telemetry/PublicAPI/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/ExtensionResources.Designer.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/ExtensionResources.Designer.cs new file mode 100644 index 0000000000..b9acf27546 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/ExtensionResources.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Testing.Extensions.Telemetry.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ExtensionResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ExtensionResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Testing.Extensions.Telemetry.Resources.ExtensionResources", typeof(ExtensionResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders. + /// + internal static string AddAppInsightsTelemetryProviderInvalidBuilder { + get { + return ResourceManager.GetString("AddAppInsightsTelemetryProviderInvalidBuilder", resourceCulture); + } + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/ExtensionResources.resx b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/ExtensionResources.resx new file mode 100644 index 0000000000..7a0e29405e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/ExtensionResources.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.cs.xlf new file mode 100644 index 0000000000..c76b3dfbad --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.cs.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + Zprostředkovatele telemetrie AppInsights jde zaregistrovat jenom do tvůrců Microsoft.Testing.Platform.Builder.TestApplicationBuilder. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.de.xlf new file mode 100644 index 0000000000..1dc6dae7ed --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.de.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + Der AppInsights-Telemetrieanbieter kann nur bei Microsoft.Testing.Platform.Builder.TestApplicationBuilder-Builders registriert werden. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.es.xlf new file mode 100644 index 0000000000..eb509d882d --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.es.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + El proveedor de telemetría AppInsights solo se puede registrar en los generadores Microsoft.Testing.Platform.Builder.TestApplicationBuilder. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.fr.xlf new file mode 100644 index 0000000000..8c88c9a474 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.fr.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + Le fournisseur de données de télémétrie AppInsights ne peut être inscrit qu’auprès des générateurs Microsoft.Testing.Platform.Builder.TestApplicationBuilder + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.it.xlf new file mode 100644 index 0000000000..fc4bb6ca2c --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.it.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + Il provider di telemetria AppInsights può essere registrato solo nei generatori Microsoft.Testing.Platform.Builder.TestApplicationBuilder + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.ja.xlf new file mode 100644 index 0000000000..4bc11711f0 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.ja.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + AppInsights テレメトリ プロバイダーは、Microsoft.Testing.Platform.Builder.TestApplicationBuilder ビルダーにのみ登録できます + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.ko.xlf new file mode 100644 index 0000000000..da0bd7b768 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.ko.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + AppInsights 원격 분석 공급자는 Microsoft.Testing.Platform.Builder.TestApplicationBuilder 빌더에만 등록할 수 있습니다. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.pl.xlf new file mode 100644 index 0000000000..3ba5637fb1 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.pl.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + Dostawcę telemetrii usługi AppInsights można zarejestrować tylko w konstruktorach Microsoft.Testing.Platform.Builder.TestApplicationBuilder + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.pt-BR.xlf new file mode 100644 index 0000000000..465eb1bd5e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.pt-BR.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + O provedor de telemetria do AppInsights só pode ser registrado nos construtores Microsoft.Testing.Platform.Builder.TestApplicationBuilder + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.ru.xlf new file mode 100644 index 0000000000..aaaf1e7601 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.ru.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + Поставщик телеметрии AppInsights можно зарегистрировать только в построителях Microsoft.Testing.Platform.Builder.TestApplicationBuilder. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.tr.xlf new file mode 100644 index 0000000000..7a81b867a7 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.tr.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + AppInsights telemetri sağlayıcısı yalnızca Microsoft.Testing.Platform.Builder.TestApplicationBuilder oluşturucularına kaydedilebilir + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.zh-Hans.xlf new file mode 100644 index 0000000000..0647a34712 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.zh-Hans.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + AppInsights 遥测提供程序只能注册到 Microsoft.Testing.Platform.Builder.TestApplicationBuilder 生成器 + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.zh-Hant.xlf new file mode 100644 index 0000000000..235c789cda --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/Resources/xlf/ExtensionResources.zh-Hant.xlf @@ -0,0 +1,12 @@ + + + + + + AppInsights telemetry provider can only be registered onto Microsoft.Testing.Platform.Builder.TestApplicationBuilder builders + AppInsights 遙測提供者只能註冊到 Microsoft.Testing.Platform.Builder.TestApplicationBuilder 建立器 + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/TestingPlatformBuilderHook.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/TestingPlatformBuilderHook.cs new file mode 100644 index 0000000000..3de2580d6a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/TestingPlatformBuilderHook.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Builder; + +namespace Microsoft.Testing.Extensions.Telemetry; + +public static class TestingPlatformBuilderHook +{ + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _) + => testApplicationBuilder.AddAppInsightsTelemetryProvider(); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/build/Microsoft.Testing.Extensions.Telemetry.props b/src/Platform/Microsoft.Testing.Extensions.Telemetry/build/Microsoft.Testing.Extensions.Telemetry.props new file mode 100644 index 0000000000..db2f9e299b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/build/Microsoft.Testing.Extensions.Telemetry.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/buildMultiTargeting/Microsoft.Testing.Extensions.Telemetry.props b/src/Platform/Microsoft.Testing.Extensions.Telemetry/buildMultiTargeting/Microsoft.Testing.Extensions.Telemetry.props new file mode 100644 index 0000000000..b3ffdb0d68 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/buildMultiTargeting/Microsoft.Testing.Extensions.Telemetry.props @@ -0,0 +1,9 @@ + + + + + Microsoft.Testing.Extensions.Telemetry + Microsoft.Testing.Extensions.Telemetry.TestingPlatformBuilderHook + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/buildTransitive/Microsoft.Testing.Extensions.Telemetry.props b/src/Platform/Microsoft.Testing.Extensions.Telemetry/buildTransitive/Microsoft.Testing.Extensions.Telemetry.props new file mode 100644 index 0000000000..db2f9e299b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/buildTransitive/Microsoft.Testing.Extensions.Telemetry.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/BannedSymbols.txt new file mode 100644 index 0000000000..ea8617fcb0 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/BannedSymbols.txt @@ -0,0 +1,10 @@ +T:System.ArgumentNullException; Use 'ArgumentGuard' instead +P:System.DateTime.Now; Use 'IClock' instead +P:System.DateTime.UtcNow; Use 'IClock' instead +M:System.Threading.Tasks.Task.Run(System.Action); Use 'ITask' instead +M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask' instead +M:System.Threading.Tasks.Task.WhenAll(System.Collections.Generic.IEnumerable{System.Threading.Tasks.Task}); Use 'ITask' instead +M:System.String.IsNullOrEmpty(System.String); Use 'RoslynString.IsNullOrEmpty' instead +M:System.String.IsNullOrWhiteSpace(System.String); Use 'RoslynString.IsNullOrWhiteSpace' instead +M:System.Diagnostics.Debug.Assert(System.Boolean); Use 'RoslynDebug.Assert' instead +M:System.Diagnostics.Debug.Assert(System.Boolean,System.String); Use 'RoslynDebug.Assert' instead diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/GlobalSuppressions.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/GlobalSuppressions.cs new file mode 100644 index 0000000000..91e972bf72 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/GlobalSuppressions.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +// We usually do not want to suppress issues through GlobalSuppressions file but in this case we have to do it because +// we are not able to suppress the issue differently. +#pragma warning disable IDE0076 +[assembly: SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "Source-generated file that cannot match analyzers requirements", Scope = "member", Target = "~M:Polyfill.CancelAsync(System.Threading.CancellationTokenSource)~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "Source-generated file that cannot match analyzers requirements", Scope = "member", Target = "~M:System.Reflection.NullabilityInfoContext.CheckParameterMetadataType(System.Reflection.ParameterInfo,System.Reflection.NullabilityInfo)")] +[assembly: SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "Source-generated file that cannot match analyzers requirements", Scope = "member", Target = "~M:System.Reflection.NullabilityInfoContext.Create(System.Reflection.EventInfo)~System.Reflection.NullabilityInfo")] +[assembly: SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "Source-generated file that cannot match analyzers requirements", Scope = "member", Target = "~M:System.Reflection.NullabilityInfoContext.Create(System.Reflection.FieldInfo)~System.Reflection.NullabilityInfo")] +[assembly: SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "Source-generated file that cannot match analyzers requirements", Scope = "member", Target = "~M:System.Reflection.NullabilityInfoContext.Create(System.Reflection.ParameterInfo)~System.Reflection.NullabilityInfo")] +[assembly: SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "Source-generated file that cannot match analyzers requirements", Scope = "member", Target = "~M:System.Reflection.NullabilityInfoContext.Create(System.Reflection.PropertyInfo)~System.Reflection.NullabilityInfo")] +#pragma warning restore IDE0076 diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/ITrxReportCapability.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/ITrxReportCapability.cs new file mode 100644 index 0000000000..77d33fb35a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/ITrxReportCapability.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Capabilities.TestFramework; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; + +/// +/// This capability is used to indicate whether or not the test framework supports trx report generation. +/// By supporting trx generation, the test adapter should ensure that some required properties are available +/// for all the nodes. +/// We expect these properties in the node bag: +/// - 1 trxreport.classname +/// - 0..n trxreport.testcategory +/// And, in case of exception, the following extra properties: +/// - trxreport.exceptionmessage +/// - trxreport.exceptionstacktrace. +/// +public interface ITrxReportCapability : ITestFrameworkCapability +{ + /// + /// Gets a value indicating whether indicates if the test framework supports trx report properties enrichment. + /// + bool IsSupported { get; } + + /// + /// Notifies the test framework that the trx report is enabled and trx report properties should be added to the test nodes. + /// + void Enable(); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/Microsoft.Testing.Extensions.TrxReport.Abstractions.csproj b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/Microsoft.Testing.Extensions.TrxReport.Abstractions.csproj new file mode 100644 index 0000000000..3846c32606 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/Microsoft.Testing.Extensions.TrxReport.Abstractions.csproj @@ -0,0 +1,30 @@ + + + + netstandard2.0;$(MicrosoftTestingTargetFrameworks) + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PACKAGE.md b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PACKAGE.md new file mode 100644 index 0000000000..19953e6fef --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PACKAGE.md @@ -0,0 +1,11 @@ +# Microsoft.Testing + +Microsoft Testing is a set of platform, framework and protocol intended to make it possible to run any test on any target or device. + +Documentation can be found at . + +## About + +This package provides interfaces and data objects used by extensions willing to interoperate with TRX functionality. + +This package does not bring support for telemetry, for this you would need to install Microsoft.Testing.Extensions.TrxReport package. diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..a841a60ade --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/PublicAPI.Shipped.txt @@ -0,0 +1,99 @@ +#nullable enable +Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage +Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage.DebugOrTraceTrxMessage(string? Message) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage.Deconstruct(out string? Message) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage.Equals(Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage? other) -> bool +Microsoft.Testing.Extensions.TrxReport.Abstractions.ITrxReportCapability +Microsoft.Testing.Extensions.TrxReport.Abstractions.ITrxReportCapability.Enable() -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.ITrxReportCapability.IsSupported.get -> bool +Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage +Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage.Deconstruct(out string? Message) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage.Equals(Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage? other) -> bool +Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage.StandardErrorTrxMessage(string? Message) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage +Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage.Deconstruct(out string? Message) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage.Equals(Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage? other) -> bool +Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage.StandardOutputTrxMessage(string? Message) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty.$() -> Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty! +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty.Categories.get -> string![]! +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty.Categories.init -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty.Deconstruct(out string![]! Categories) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty.Equals(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty? other) -> bool +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty.TrxCategoriesProperty(string![]! Categories) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.$() -> Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty! +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.Deconstruct(out string? Message, out string? StackTrace) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.Equals(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty? other) -> bool +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.Message.get -> string? +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.Message.init -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.StackTrace.get -> string? +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.StackTrace.init -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.TrxExceptionProperty(string? Message, string? StackTrace) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty.$() -> Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty! +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty.Deconstruct(out string! FullyQualifiedTypeName) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty.Equals(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty? other) -> bool +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty.FullyQualifiedTypeName.get -> string! +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty.FullyQualifiedTypeName.init -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty.TrxFullyQualifiedTypeNameProperty(string! FullyQualifiedTypeName) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.Deconstruct(out string? Message) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.Message.get -> string? +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.Message.init -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.TrxMessage(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage! original) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.TrxMessage(string? Message) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty.$() -> Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty! +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty.Deconstruct(out Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage![]! Messages) -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty.Equals(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty? other) -> bool +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty.Messages.get -> Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage![]! +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty.Messages.init -> void +Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty.TrxMessagesProperty(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage![]! Messages) -> void +override Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage.Equals(object? obj) -> bool +override Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage.GetHashCode() -> int +override Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage.ToString() -> string! +override Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage.Equals(object? obj) -> bool +override Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage.GetHashCode() -> int +override Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage.ToString() -> string! +override Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage.Equals(object? obj) -> bool +override Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage.GetHashCode() -> int +override Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage.ToString() -> string! +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty.Equals(object? obj) -> bool +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty.GetHashCode() -> int +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty.ToString() -> string! +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.Equals(object? obj) -> bool +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.GetHashCode() -> int +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.ToString() -> string! +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty.Equals(object? obj) -> bool +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty.GetHashCode() -> int +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty.ToString() -> string! +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.Equals(object? obj) -> bool +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.GetHashCode() -> int +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.ToString() -> string! +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty.Equals(object? obj) -> bool +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty.GetHashCode() -> int +override Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty.ToString() -> string! +override sealed Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage.Equals(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage? other) -> bool +override sealed Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage.Equals(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage? other) -> bool +override sealed Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage.Equals(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage? other) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage.operator !=(Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage.operator ==(Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage.operator !=(Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage.operator ==(Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage.operator !=(Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage.operator ==(Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty.operator !=(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty.operator ==(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxCategoriesProperty? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.operator !=(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty.operator ==(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxExceptionProperty? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty.operator !=(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty.operator ==(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxFullyQualifiedTypeNameProperty? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.operator !=(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.operator ==(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty.operator !=(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty? right) -> bool +static Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty.operator ==(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty? left, Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessagesProperty? right) -> bool +virtual Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.$() -> Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage! +virtual Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.EqualityContract.get -> System.Type! +virtual Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.Equals(Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage? other) -> bool +virtual Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage.PrintMembers(System.Text.StringBuilder! builder) -> bool diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/net/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/net/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..c9a999b27e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/net/PublicAPI.Shipped.txt @@ -0,0 +1,4 @@ +#nullable enable +override Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage.$() -> Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage! +override Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage.$() -> Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage! +override Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage.$() -> Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage! diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/net/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/net/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/net/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..af59c85a51 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt @@ -0,0 +1,4 @@ +#nullable enable +override Microsoft.Testing.Extensions.TrxReport.Abstractions.DebugOrTraceTrxMessage.$() -> Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage! +override Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardErrorTrxMessage.$() -> Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage! +override Microsoft.Testing.Extensions.TrxReport.Abstractions.StandardOutputTrxMessage.$() -> Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxMessage! diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/TrxReportProperties.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/TrxReportProperties.cs new file mode 100644 index 0000000000..13566d2228 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/TrxReportProperties.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Extensions.Messages; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; + +public sealed record class TrxExceptionProperty(string? Message, string? StackTrace) : IProperty; + +public sealed record class TrxFullyQualifiedTypeNameProperty(string FullyQualifiedTypeName) : IProperty; + +public record class TrxMessage(string? Message); + +public sealed record class StandardErrorTrxMessage(string? Message) : TrxMessage(Message); + +public sealed record class StandardOutputTrxMessage(string? Message) : TrxMessage(Message); + +public sealed record class DebugOrTraceTrxMessage(string? Message) : TrxMessage(Message); + +public sealed record class TrxMessagesProperty(TrxMessage[] Messages) : IProperty; + +public sealed record class TrxCategoriesProperty(string[] Categories) : IProperty; diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.TrxReport/BannedSymbols.txt new file mode 100644 index 0000000000..ea8617fcb0 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/BannedSymbols.txt @@ -0,0 +1,10 @@ +T:System.ArgumentNullException; Use 'ArgumentGuard' instead +P:System.DateTime.Now; Use 'IClock' instead +P:System.DateTime.UtcNow; Use 'IClock' instead +M:System.Threading.Tasks.Task.Run(System.Action); Use 'ITask' instead +M:System.Threading.Tasks.Task.WhenAll(System.Threading.Tasks.Task[]); Use 'ITask' instead +M:System.Threading.Tasks.Task.WhenAll(System.Collections.Generic.IEnumerable{System.Threading.Tasks.Task}); Use 'ITask' instead +M:System.String.IsNullOrEmpty(System.String); Use 'RoslynString.IsNullOrEmpty' instead +M:System.String.IsNullOrWhiteSpace(System.String); Use 'RoslynString.IsNullOrWhiteSpace' instead +M:System.Diagnostics.Debug.Assert(System.Boolean); Use 'RoslynDebug.Assert' instead +M:System.Diagnostics.Debug.Assert(System.Boolean,System.String); Use 'RoslynDebug.Assert' instead diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Microsoft.Testing.Extensions.TrxReport.csproj b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Microsoft.Testing.Extensions.TrxReport.csproj new file mode 100644 index 0000000000..63dfed24c7 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Microsoft.Testing.Extensions.TrxReport.csproj @@ -0,0 +1,73 @@ + + + + netstandard2.0;$(MicrosoftTestingTargetFrameworks) + Microsoft.Testing.Extensions.TestReports + + $(DefineConstants);USE_TRX_NAMESPACE + + + + + + + + + + + + + + true + buildMultiTargeting + + + buildTransitive/$(TargetFramework) + + + build/$(TargetFramework) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + ExtensionResources.resx + + + + + + ResXFileCodeGenerator + ExtensionResources.Designer.cs + + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/PACKAGE.md b/src/Platform/Microsoft.Testing.Extensions.TrxReport/PACKAGE.md new file mode 100644 index 0000000000..69685e9b2b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/PACKAGE.md @@ -0,0 +1,9 @@ +# Microsoft.Testing + +Microsoft Testing is a set of platform, framework and protocol intended to make it possible to run any test on any target or device. + +Documentation can be found at . + +## About + +This package extends Microsoft Testing Platform to provide TRX test reports. diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.TrxReport/PublicAPI/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..ef7f0a192d --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/PublicAPI/PublicAPI.Shipped.txt @@ -0,0 +1,5 @@ +#nullable enable +Microsoft.Testing.Extensions.TrxReport.TestingPlatformBuilderHook +Microsoft.Testing.Extensions.TrxReportExtensions +static Microsoft.Testing.Extensions.TrxReport.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! _) -> void +static Microsoft.Testing.Extensions.TrxReportExtensions.AddTrxReportProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.TrxReport/PublicAPI/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/ExtensionResources.Designer.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/ExtensionResources.Designer.cs new file mode 100644 index 0000000000..55793cbf17 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/ExtensionResources.Designer.cs @@ -0,0 +1,252 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Testing.Extensions.TestReports.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ExtensionResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ExtensionResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Testing.Extensions.TestReports.Resources.ExtensionResources", typeof(ExtensionResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder'. + /// + internal static string InvalidTestApplicationBuilderType { + get { + return ResourceManager.GetString("InvalidTestApplicationBuilderType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The baseline TRX file. + /// + internal static string TrxComparerToolBaselineFileOptionDescription { + get { + return ResourceManager.GetString("TrxComparerToolBaselineFileOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '--{0}' and '--{1}' must both be specified. + /// + internal static string TrxComparerToolBothFilesMustBeSpecified { + get { + return ResourceManager.GetString("TrxComparerToolBothFilesMustBeSpecified", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This tool allows to compare and highights differences between 2 TRX reports. + /// + internal static string TrxComparerToolDescription { + get { + return ResourceManager.GetString("TrxComparerToolDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to TRX comparer tool. + /// + internal static string TrxComparerToolDisplayName { + get { + return ResourceManager.GetString("TrxComparerToolDisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '--{0}' expects a single trx file path as argument. + /// + internal static string TrxComparerToolOptionExpectsSingleArgument { + get { + return ResourceManager.GetString("TrxComparerToolOptionExpectsSingleArgument", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The TRX file to compare with the baseline. + /// + internal static string TrxComparerToolOtherFileOptionDescription { + get { + return ResourceManager.GetString("TrxComparerToolOtherFileOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Test session. + /// + internal static string TrxReportArtifactDescription { + get { + return ResourceManager.GetString("TrxReportArtifactDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to TRX Report. + /// + internal static string TrxReportArtifactDisplayName { + get { + return ResourceManager.GetString("TrxReportArtifactDisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx). + /// + internal static string TrxReportFileNameExtensionIsNotTrx { + get { + return ResourceManager.GetString("TrxReportFileNameExtensionIsNotTrx", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The name of the generated TRX report. + /// + internal static string TrxReportFileNameOptionDescription { + get { + return ResourceManager.GetString("TrxReportFileNameOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '--report-trx-filename' requires '--report-trx' to be enabled. + /// + internal static string TrxReportFileNameRequiresTrxReport { + get { + return ResourceManager.GetString("TrxReportFileNameRequiresTrxReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to file name argument must not contain path (e.g. --report-trx-filename myreport.trx). + /// + internal static string TrxReportFileNameShouldNotContainPath { + get { + return ResourceManager.GetString("TrxReportFileNameShouldNotContainPath", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports. + /// + internal static string TrxReportFrameworkDoesNotSupportTrxReportCapability { + get { + return ResourceManager.GetString("TrxReportFrameworkDoesNotSupportTrxReportCapability", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync'. + /// + internal static string TrxReportGeneratorBeforeTestHostProcessStartAsyncNotCalled { + get { + return ResourceManager.GetString("TrxReportGeneratorBeforeTestHostProcessStartAsyncNotCalled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Produce a TRX report for the current test session. + /// + internal static string TrxReportGeneratorDescription { + get { + return ResourceManager.GetString("TrxReportGeneratorDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to TRX report generator. + /// + internal static string TrxReportGeneratorDisplayName { + get { + return ResourceManager.GetString("TrxReportGeneratorDisplayName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Required environment variable '{0}' is missing for TRX report generator. + /// + internal static string TrxReportGeneratorMissingTrxNamedPipeEnvironmentVariable { + get { + return ResourceManager.GetString("TrxReportGeneratorMissingTrxNamedPipeEnvironmentVariable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '--report-trx' cannot be enabled when using '--list-tests'. + /// + internal static string TrxReportIsNotValidForDiscovery { + get { + return ResourceManager.GetString("TrxReportIsNotValidForDiscovery", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable generating TRX report. + /// + internal static string TrxReportOptionDescription { + get { + return ResourceManager.GetString("TrxReportOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Requests of type '{0}' is not supported. + /// + internal static string UnsupportedRequestTypeErrorMessage { + get { + return ResourceManager.GetString("UnsupportedRequestTypeErrorMessage", resourceCulture); + } + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/ExtensionResources.resx b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/ExtensionResources.resx new file mode 100644 index 0000000000..08ffbb226f --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/ExtensionResources.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + + + The baseline TRX file + + + '--{0}' and '--{1}' must both be specified + + + This tool allows to compare and highights differences between 2 TRX reports + + + TRX comparer tool + + + '--{0}' expects a single trx file path as argument + + + The TRX file to compare with the baseline + + + Test session + + + TRX Report + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + + + The name of the generated TRX report + + + '--report-trx-filename' requires '--report-trx' to be enabled + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + + + Produce a TRX report for the current test session + + + TRX report generator + + + Required environment variable '{0}' is missing for TRX report generator + + + '--report-trx' cannot be enabled when using '--list-tests' + + + Enable generating TRX report + + + Requests of type '{0}' is not supported + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.cs.xlf new file mode 100644 index 0000000000..240d84bd22 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.cs.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + Generátor sestav TRX funguje pouze s tvůrci typu Microsoft.Testing.Platform.Builder.TestApplicationBuilder. + + + + The baseline TRX file + Soubor TRX standardních hodnot + + + + '--{0}' and '--{1}' must both be specified + Musí být zadaná jak možnost --{0}, tak možnost --{1}. + + + + This tool allows to compare and highights differences between 2 TRX reports + Tento nástroj umožňuje porovnat rozdíly mezi 2 sestavami TRX a zobrazit přehled těchto rozdílů. + + + + TRX comparer tool + Porovnávač souborů TRX + + + + '--{0}' expects a single trx file path as argument + --{0} očekává jako argument jednu cestu k souboru trx. + + + + The TRX file to compare with the baseline + Soubor TRX, který se má porovnat se standardními hodnotami + + + + Test session + Relace testu + + + + TRX Report + Sestava TRX + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + Argument názvu souboru --report-trx-filename musí končit na .trx (například --report-trx-filename myreport.trx). + + + + The name of the generated TRX report + Název vygenerované sestavy TRX + + + + '--report-trx-filename' requires '--report-trx' to be enabled + --report-trx-filename vyžaduje, aby byla povolená možnost --report-trx. + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + Argument názvu souboru nesmí obsahovat cestu (například --report-trx-filename myreport.trx). + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + Testovací architektura {0} s UID {1} nepodporuje ITrxReportCapability, což vede k chybějícím nebo neúplným sestavám TRX. + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + Před metodou OnTestHostProcessStartedAsync musí být volána metoda BeforeTestHostProcessStartAsync. + + + + Produce a TRX report for the current test session + Vytvořit sestavu TRX pro aktuální testovací relaci + + + + TRX report generator + Generátor sestav TRX + + + + Required environment variable '{0}' is missing for TRX report generator + Chybí požadovaná proměnná prostředí {0} pro generátor sestav TRX. + + + + '--report-trx' cannot be enabled when using '--list-tests' + Možnost --report-trx nejde povolit, pokud se používá možnost --list-tests. + + + + Enable generating TRX report + Povolit vygenerování sestavy TRX + + + + Requests of type '{0}' is not supported + Žádosti typu {0} nejsou podporovány. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.de.xlf new file mode 100644 index 0000000000..9765711f6f --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.de.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + Der TRX-Bericht-Generator funktioniert nur mit Generatoren vom Typ "Microsoft.Testing.Platform.Builder.TestApplicationBuilder" + + + + The baseline TRX file + Die Baseline-TRX-Datei + + + + '--{0}' and '--{1}' must both be specified + "--{0}" und "--{1}" müssen beide angegeben werden + + + + This tool allows to compare and highights differences between 2 TRX reports + Dieses Tool ermöglicht Vergleichs- und Highights-Unterschiede zwischen 2 TRX-Berichten + + + + TRX comparer tool + TRX-Vergleichstool + + + + '--{0}' expects a single trx file path as argument + "--{0}" erwartet einen einzelnen TRX-Dateipfad als Argument + + + + The TRX file to compare with the baseline + Die TRX-Datei, die mit der Baseline verglichen werden soll + + + + Test session + Testsitzung + + + + TRX Report + TRX-Bericht + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + Das Dateinamenargument "--report-trx-filename" muss auf ".trx" enden (z. B. "--report-trx-filename myreport.trx") + + + + The name of the generated TRX report + Der Name des generierten TRX-Berichts + + + + '--report-trx-filename' requires '--report-trx' to be enabled + "--report-trx-filename" muss "--report-trx" muss aktiviert sein + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + Das Dateinamenargument darf keinen Pfad enthalten (z. B. --report-trx-filename myreport.trx). + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + Das Testframework "{0}" mit UID "{1}" unterstützt "ITrxReportCapability" nicht, was zu fehlenden oder unvollständigen TRX-Berichten führt. + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + Die Methode "BeforeTestHostProcessStartAsync" muss vor "OnTestHostProcessStartedAsync" aufgerufen werden + + + + Produce a TRX report for the current test session + Erstellt einen TRX-Bericht für die aktuelle Testsitzung + + + + TRX report generator + TRX-Bericht-Generator + + + + Required environment variable '{0}' is missing for TRX report generator + Die erforderliche Umgebungsvariable "{0}" fehlt für den TRX-Bericht-Generator. + + + + '--report-trx' cannot be enabled when using '--list-tests' + "--report-trx" kann bei Verwendung von "--list-tests" nicht aktiviert werden + + + + Enable generating TRX report + Generieren des TRX-Berichts aktivieren + + + + Requests of type '{0}' is not supported + Anforderungen vom Typ "{0}" werden nicht unterstützt + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.es.xlf new file mode 100644 index 0000000000..2a9d69ee4b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.es.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + El generador de informes TRX solo funciona con generadores de tipo "Microsoft.Testing.Platform.Builder.TestApplicationBuilder". + + + + The baseline TRX file + El archivo TRX de línea de base + + + + '--{0}' and '--{1}' must both be specified + '--{0}' y '--{1}' deben especificarse + + + + This tool allows to compare and highights differences between 2 TRX reports + Esta herramienta permite comparar y comparar las diferencias entre 2 informes TRX + + + + TRX comparer tool + Herramienta de comparador TRX + + + + '--{0}' expects a single trx file path as argument + '--{0}' espera una única ruta de acceso de archivo trx como argumento + + + + The TRX file to compare with the baseline + Archivo TRX que se va a comparar con la línea base + + + + Test session + Sesión de prueba + + + + TRX Report + Informe TRX + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + El argumento de nombre de archivo '--report-trx-filename' debe terminar con '.trx' (por ejemplo, --report-trx-filename myreport.trx) + + + + The name of the generated TRX report + Nombre del informe TRX generado + + + + '--report-trx-filename' requires '--report-trx' to be enabled + '--report-trx-filename' requiere que se habilite '--report-trx' + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + el argumento de nombre de archivo no debe contener la ruta de acceso (por ejemplo, --report-trx-filename myreport.trx) + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + El marco de pruebas '{0}' con UID '{1}' no admite 'ITrxReportCapability' que conduce a informes TRX que faltan o están incompletos + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + Se debe llamar al método 'BeforeTestHostProcessStartAsync' antes de 'OnTestHostProcessStartedAsync' + + + + Produce a TRX report for the current test session + Generar un informe TRX para la sesión de prueba actual + + + + TRX report generator + Generador de informes TRX + + + + Required environment variable '{0}' is missing for TRX report generator + Falta la variable de entorno necesaria '{0}' para el generador de informes TRX + + + + '--report-trx' cannot be enabled when using '--list-tests' + '--report-trx' no se puede habilitar cuando se usa '--list-tests' + + + + Enable generating TRX report + Habilitación de la generación de informes TRX + + + + Requests of type '{0}' is not supported + No se admiten solicitudes de tipo '{0}' + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.fr.xlf new file mode 100644 index 0000000000..d13aa67023 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.fr.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + Le générateur de rapports TRX fonctionne uniquement avec les générateurs de type « Microsoft.Testing.Platform.Builder.TestApplicationBuilder » + + + + The baseline TRX file + Fichier TRX de référence + + + + '--{0}' and '--{1}' must both be specified + '--{0}' et '--{1}' doivent tous deux être spécifiés + + + + This tool allows to compare and highights differences between 2 TRX reports + Cet outil permet de comparer et d’afficher des différences élevées entre 2 rapports TRX + + + + TRX comparer tool + Outil comparateur TRX + + + + '--{0}' expects a single trx file path as argument + '--{0}' attend un seul chemin de fichier trx comme argument + + + + The TRX file to compare with the baseline + Fichier TRX à comparer à la ligne de base + + + + Test session + Session de test + + + + TRX Report + Rapport TRX + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + L'argument du nom de fichier '--report-trx-filename' doit se terminer par '.trx' (par exemple --report-trx-filename myreport.trx) + + + + The name of the generated TRX report + Nom du rapport TRX généré + + + + '--report-trx-filename' requires '--report-trx' to be enabled + '--report-trx-filename' nécessite que '--report-trx' soit activé + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + L'argument du nom de fichier ne doit pas contenir de chemin (par exemple --report-trx-filename myreport.trx) + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + Le framework de test '{0}' avec l'UID '{1}' ne prend pas en charge 'ITrxReportCapability', ce qui entraîne des rapports TRX manquants ou incomplets + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + La méthode 'BeforeTestHostProcessStartAsync' doit être appelée avant 'OnTestHostProcessStartedAsync' + + + + Produce a TRX report for the current test session + Produire un rapport TRX pour la session de test actuelle + + + + TRX report generator + Générateur de rapports TRX + + + + Required environment variable '{0}' is missing for TRX report generator + La variable d’environnement requise «{0}» est manquante pour le générateur de rapports TRX + + + + '--report-trx' cannot be enabled when using '--list-tests' + '--report-trx' ne peut pas être activé lors de l'utilisation de '--list-tests' + + + + Enable generating TRX report + Activer la génération du rapport TRX + + + + Requests of type '{0}' is not supported + Les demandes de type «{0}» ne sont pas prises en charge + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.it.xlf new file mode 100644 index 0000000000..985e0946d3 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.it.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + Il generatore di report TRX funziona solo con i generatori di tipo 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder'. + + + + The baseline TRX file + File TRX di base + + + + '--{0}' and '--{1}' must both be specified + È necessario specificare sia '--{0}' che '--{1}' + + + + This tool allows to compare and highights differences between 2 TRX reports + Questo strumento consente di confrontare e confrontare le differenze di vista tra 2 report TRX + + + + TRX comparer tool + Strumento di confronto TRX + + + + '--{0}' expects a single trx file path as argument + '--{0}' prevede un singolo percorso di file trx come argomento + + + + The TRX file to compare with the baseline + File TRX da confrontare con la baseline + + + + Test session + Sessione di test + + + + TRX Report + Report TRX + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + L'argomento del nome del file '--report-trx-filename' deve terminare con '.trx' (ad esempio --report-trx-filename myreport.trx) + + + + The name of the generated TRX report + Nome del report TRX generato + + + + '--report-trx-filename' requires '--report-trx' to be enabled + '--report-trx-filename' richiede l'abilitazione di '--report-trx' + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + l'argomento del nome del file non deve contenere il percorso (ad esempio --report-trx-filename myreport.trx) + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + Il framework di test '{0}' con UID '{1}' non supporta 'ITrxReportCapability' causando report TRX mancanti o incompleti + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + Il metodo 'BeforeTestHostProcessStartAsync' deve essere chiamato prima di 'OnTestHostProcessStartedAsync'. + + + + Produce a TRX report for the current test session + Generare un rapporto TRX per la sessione di test corrente. + + + + TRX report generator + Generatore di report TRX + + + + Required environment variable '{0}' is missing for TRX report generator + Variabile di ambiente obbligatoria '{0}' mancante per il generatore di report TRX + + + + '--report-trx' cannot be enabled when using '--list-tests' + Non è possibile abilitare '--report-trx' quando si usa '--list-tests' + + + + Enable generating TRX report + Abilitare la generazione del report TRX + + + + Requests of type '{0}' is not supported + Le richieste di tipo '{0}' non sono supportate + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ja.xlf new file mode 100644 index 0000000000..418cf99719 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ja.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + TRX レポート ジェネレーター、'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' 型のビルダーでのみ機能します + + + + The baseline TRX file + ベースライン TRX ファイル + + + + '--{0}' and '--{1}' must both be specified + '--{0}' と '--{1}' の両方を指定する必要があります + + + + This tool allows to compare and highights differences between 2 TRX reports + このツールを使用すると、2 つの TRX レポートの違いを比較し、強調表示できます + + + + TRX comparer tool + TRX 比較ツール + + + + '--{0}' expects a single trx file path as argument + '--{0}' には、引数として 1 つの trx ファイル パスが必要です + + + + The TRX file to compare with the baseline + ベースラインと比較する TRX ファイル + + + + Test session + テスト セッション + + + + TRX Report + TRX レポート + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + '--report-trx-filename' ファイル名引数の末尾は '.trx' にする必要があります (例: --report-trx-filename myreport.trx) + + + + The name of the generated TRX report + 生成された TRX レポートの名前 + + + + '--report-trx-filename' requires '--report-trx' to be enabled + '--report-trx-filename' では、'--report-trx' を有効にする必要があります + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + ファイル名引数にパスを含めることはできません (例: --report-trx-filename myreport.trx) + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + UID '{1}' のテスト フレームワーク '{0}' は 'ITrxReportCapability' をサポートしていないため、TRX レポートが見つからないか不完全です + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + 'OnTestHostProcessStartedAsync' の前にメソッド 'BeforeTestHostProcessStartAsync' を呼び出す必要があります + + + + Produce a TRX report for the current test session + 現在のテスト セッションの TRX レポートを生成する + + + + TRX report generator + TRX レポート ジェネレーター + + + + Required environment variable '{0}' is missing for TRX report generator + TRX レポート ジェネレーターに必要な環境変数 '{0}' がありません + + + + '--report-trx' cannot be enabled when using '--list-tests' + '--list-tests' を使用している場合、'--report-trx' を有効にすることはできません + + + + Enable generating TRX report + TRX レポートの生成を有効にする + + + + Requests of type '{0}' is not supported + 型 '{0}' の要求はサポートされていません + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ko.xlf new file mode 100644 index 0000000000..3ee998b1ad --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ko.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + TRX 보고서 생성기는 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' 유형의 작성기에서만 작동합니다. + + + + The baseline TRX file + 기준 TRX 파일 + + + + '--{0}' and '--{1}' must both be specified + '--{0}' 및 '--{1}'을(를) 모두 지정해야 합니다. + + + + This tool allows to compare and highights differences between 2 TRX reports + 이 도구를 사용하면 2개의 TRX 보고서 간의 차이점을 비교하고 강조할 수 있습니다. + + + + TRX comparer tool + TRX 비교자 도구 + + + + '--{0}' expects a single trx file path as argument + '--{0}'에는 단일 trx 파일 경로가 인수로 필요합니다. + + + + The TRX file to compare with the baseline + 기준과 비교할 TRX 파일입니다. + + + + Test session + 테스트 세션 + + + + TRX Report + TRX 보고서 + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + '--report-trx-filename' 파일 이름 인수는 '.trx'로 끝나야 합니다(예: --report-trx-filename myreport.trx). + + + + The name of the generated TRX report + 생성된 TRX 보고서의 이름입니다. + + + + '--report-trx-filename' requires '--report-trx' to be enabled + '--report-trx-filename'을 사용하려면 '--report-trx'를 사용하도록 설정해야 합니다. + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + 파일 이름 인수는 경로를 포함해서는 안 됩니다(예: --report-trx-filename myreport.trx). + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + UID가 '{1}'인 테스트 프레임워크 '{0}'은(는) 'ITrxReportCapability'를 지원하지 않으므로 TRX 보고서가 없거나 불완전합니다. + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + 'BeforeTestHostProcessStartAsync' 메서드는 'OnTestHostProcessStartedAsync' 전에 호출해야 합니다. + + + + Produce a TRX report for the current test session + 현재 테스트 세션에 대한 TRX 보고서 생성 + + + + TRX report generator + TRX 보고서 생성기 + + + + Required environment variable '{0}' is missing for TRX report generator + TRX 보고서 생성기에 필요한 환경 변수 '{0}'이(가) 없습니다. + + + + '--report-trx' cannot be enabled when using '--list-tests' + '--list-tests'를 사용하는 경우 '--report-trx'를 사용하도록 설정할 수 없습니다. + + + + Enable generating TRX report + TRX 보고서 생성 사용 + + + + Requests of type '{0}' is not supported + '{0}' 유형의 요청은 지원되지 않습니다. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pl.xlf new file mode 100644 index 0000000000..681fa340a3 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pl.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + Generator raportów TRX działa tylko z konstruktorami typu „Microsoft.Testing.Platform.Builder.TestApplicationBuilder” + + + + The baseline TRX file + Plik TRX punktu odniesienia + + + + '--{0}' and '--{1}' must both be specified + „--{0}” i „--{1}” muszą być określone + + + + This tool allows to compare and highights differences between 2 TRX reports + To narzędzie umożliwia porównywanie i zwiększanie różnic między 2 raportami TRX + + + + TRX comparer tool + Narzędzie do porównywania TRX + + + + '--{0}' expects a single trx file path as argument + Element „--{0}” oczekuje pojedynczej ścieżki pliku trx jako argumentu + + + + The TRX file to compare with the baseline + Plik TRX do porównania z punktem odniesienia + + + + Test session + Sesja testowa + + + + TRX Report + Raport TRX + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + Argument nazwy pliku „--report-trx-filename” musi kończyć się ciągiem „.trx” (np. --report-trx-filename myreport.trx) + + + + The name of the generated TRX report + Nazwa wygenerowanego raportu TRX + + + + '--report-trx-filename' requires '--report-trx' to be enabled + Element „--report-trx-filename” wymaga włączenia elementu „--report-trx” + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + argument nazwy pliku nie może zawierać ścieżki (np. --report-trx-filename myreport.trx) + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + Struktura testowa „{0}” o identyfikatorze UID „{1}” nie obsługuje elementu „ITrxReportCapability”, co prowadzi do brakujących lub niekompletnych raportów TRX + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + Metoda „BeforeTestHostProcessStartAsync” musi być wywołana przed elementem „OnTestHostProcessStartedAsync” + + + + Produce a TRX report for the current test session + Tworzenie raportu TRX dla bieżącej sesji testowej + + + + TRX report generator + Generator raportów TRX + + + + Required environment variable '{0}' is missing for TRX report generator + Brak wymaganej zmiennej środowiskowej „{0}” dla generatora raportów TRX + + + + '--report-trx' cannot be enabled when using '--list-tests' + Nie można włączyć polecenia „--report-trx” w przypadku używania polecenia „--list-tests” + + + + Enable generating TRX report + Włącz generowanie raportu TRX + + + + Requests of type '{0}' is not supported + Żądania typu „{0}” nie są obsługiwane + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pt-BR.xlf new file mode 100644 index 0000000000..8c30fe2079 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.pt-BR.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + O gerador de relatórios TRX só funciona com construtores do tipo ''Microsoft.Testing.Platform.Builder.TestApplicationBuilder'' + + + + The baseline TRX file + O arquivo TRX da linha de base + + + + '--{0}' and '--{1}' must both be specified + ''--{0}'' e ''--{1}'' devem ser especificados + + + + This tool allows to compare and highights differences between 2 TRX reports + Esta ferramenta permite comparar e definir diferenças entre dois relatórios TRX + + + + TRX comparer tool + Ferramenta de comparador TRX + + + + '--{0}' expects a single trx file path as argument + ''--{0}'' espera um único caminho de arquivo trx como argumento + + + + The TRX file to compare with the baseline + O arquivo TRX a ser comparado com a linha de base + + + + Test session + Sessão de teste + + + + TRX Report + Relatório TRX + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + O argumento de nome de arquivo ''--report-trx-filename'' deve terminar com ''.trx'' (por exemplo, --report-trx-filename myreport.trx) + + + + The name of the generated TRX report + O nome do relatório TRX gerado + + + + '--report-trx-filename' requires '--report-trx' to be enabled + ''--report-trx-filename'' requer que ''--report-trx'' esteja habilitado + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + argumento de nome de arquivo não deve conter caminho (por exemplo, --report-trx-filename myreport.trx) + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + A estrutura de teste ''{0}'' com UID ''{1}'' não dá suporte a ''ITrxReportCapability'' levando a relatórios TRX ausentes ou incompletos + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + O método ''BeforeTestHostProcessStartAsync'' deve ser chamado antes de ''OnTestHostProcessStartedAsync'' + + + + Produce a TRX report for the current test session + Produzir um relatório TRX para a sessão de teste atual + + + + TRX report generator + Gerador de relatório TRX + + + + Required environment variable '{0}' is missing for TRX report generator + A variável de ambiente necessária ''{0}'' está ausente para o gerador de relatório TRX + + + + '--report-trx' cannot be enabled when using '--list-tests' + Não é possível habilitar ''--report-trx'' ao usar ''--list-tests'' + + + + Enable generating TRX report + Habilitar geração de relatório TRX + + + + Requests of type '{0}' is not supported + Não há suporte para solicitações de tipo ''{0}'' + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ru.xlf new file mode 100644 index 0000000000..0cced45270 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.ru.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + Генератор отчетов TRX работает только с построителями типа "Microsoft.Testing.Platform.Builder.TestApplicationBuilder" + + + + The baseline TRX file + Базовый TRX-файл + + + + '--{0}' and '--{1}' must both be specified + Необходимо одновременно указать "--{0}" и "--{1}" + + + + This tool allows to compare and highights differences between 2 TRX reports + Этот инструмент позволяет сравнивать и выделять различия между двумя отчетами TRX + + + + TRX comparer tool + Средство сравнения TRX + + + + '--{0}' expects a single trx file path as argument + "--{0}" ожидает в качестве аргумента один путь к файлу TRX + + + + The TRX file to compare with the baseline + TRX-файл для сравнения с базовыми показателями + + + + Test session + Тестовый сеанс + + + + TRX Report + Отчет TRX + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + Аргумент имени файла "--report-trx-filename" должен заканчиваться на ".trx" (например, --report-trx-filename myreport.trx) + + + + The name of the generated TRX report + Имя созданного отчета TRX + + + + '--report-trx-filename' requires '--report-trx' to be enabled + Для параметра "--report-trx-filename" требуется включить "--report-trx" + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + аргумент имени файла не должен содержать путь (например, --report-trx-filename myreport.trx) + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + Платформа тестирования "{0}" с идентификатором пользователя "{1}" не поддерживает "ITrxReportCapability", поэтому отчеты TRX не будут создаваться или будут неполными + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + Метод "BeforeTestHostProcessStartAsync" должен вызываться до "OnTestHostProcessStartedAsync" + + + + Produce a TRX report for the current test session + Создавать отчет TRX для текущего тестового сеанса + + + + TRX report generator + Генератор отчетов TRX + + + + Required environment variable '{0}' is missing for TRX report generator + Для генератора отчетов TRX отсутствует необходимая переменная среды "{0}" + + + + '--report-trx' cannot be enabled when using '--list-tests' + Невозможно включить "--report-trx" при использовании "--list-tests" + + + + Enable generating TRX report + Включить создание отчета TRX + + + + Requests of type '{0}' is not supported + Запросы типа "{0}" не поддерживаются + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.tr.xlf new file mode 100644 index 0000000000..27af5d58c1 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.tr.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + TRX rapor oluşturucu yalnızca 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' türündeki oluşturucularla çalışır + + + + The baseline TRX file + Temel TRX dosyası + + + + '--{0}' and '--{1}' must both be specified + '--{0}' ve '--{1}' birlikte belirtilmelidir + + + + This tool allows to compare and highights differences between 2 TRX reports + Bu araç, 2 TRX raporu arasındaki farkların karşılaştırılmasına ve vurgulanmasına olanak tanır + + + + TRX comparer tool + TRX karşılaştırma aracı + + + + '--{0}' expects a single trx file path as argument + '--{0}', bağımsız değişken olarak tek bir trx dosya yolu bekliyor + + + + The TRX file to compare with the baseline + Taban çizgisiyle karşılaştırılacak TRX dosyası + + + + Test session + Test oturumu + + + + TRX Report + TRX Raporu + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + '--report-trx-dosyaadı' dosya adı bağımsız değişkeni '.trx' ile bitmelidir (örn. --report-trx-dosyaadı myreport.trx) + + + + The name of the generated TRX report + Oluşturulan TRX raporunun adı + + + + '--report-trx-filename' requires '--report-trx' to be enabled + '--report-trx-filename', '--report-trx'in etkinleştirilmesini gerektirir + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + dosya adı argümanı yol içermemelidir (e.g. --report-trx-filename myreport.trx) + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + UID '{0}' olan '{1}' test çerçevesi eksik veya eksik TRX raporlarına yol açarak 'ITrxReportCapability' öğesini desteklemez + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + 'BeforeTestHostProcessStartAsync' metodu 'OnTestHostProcessStartedAsync' öğesinden önce çağrılmalıdır + + + + Produce a TRX report for the current test session + Mevcut test oturumu için bir TRX raporu oluşturun + + + + TRX report generator + TRX rapor oluşturucusu + + + + Required environment variable '{0}' is missing for TRX report generator + TRX rapor oluşturucu için gerekli ortam değişkeni '{0}' eksik + + + + '--report-trx' cannot be enabled when using '--list-tests' + '--list-tests' kullanılırken '--report-trx' etkinleştirilemez + + + + Enable generating TRX report + TRX raporu oluşturmayı etkinleştirin + + + + Requests of type '{0}' is not supported + '{0}' türündeki istek desteklenmiyor + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hans.xlf new file mode 100644 index 0000000000..afefc4c42b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hans.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + TRX 报表生成器仅适用于“Microsoft.Testing.Platform.Builder.TestApplicationBuilder”类型的生成器 + + + + The baseline TRX file + 基线 TRX 文件 + + + + '--{0}' and '--{1}' must both be specified + 必须同时指定“--{0}”和“--{1}” + + + + This tool allows to compare and highights differences between 2 TRX reports + 此工具允许比较和突出显示 2 个 TRX 报表之间的差异 + + + + TRX comparer tool + TRX 比较器工具 + + + + '--{0}' expects a single trx file path as argument + “--{0}”需要单一 trx 文件路径作为参数 + + + + The TRX file to compare with the baseline + 要与基线进行比较的 TRX 文件 + + + + Test session + 测试会话 + + + + TRX Report + TRX 报表 + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + “--report-trx-filename”文件名参数必须以“.trx”结尾(例如 --report-trx-filename myreport.trx) + + + + The name of the generated TRX report + 生成的 TRX 报表的名称 + + + + '--report-trx-filename' requires '--report-trx' to be enabled + “--report-trx-filename”需要启用“--report-trx” + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + 文件名参数不得包含路径(例如 --report-trx-filename myreport.trx) + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + UID 为“{1}”的测试框架“{0}”不支持“ITrxReportCapability”,将导致 TRX 报告丢失或不完整 + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + 必须在“OnTestHostProcessStartedAsync”之前调用方法“BeforeTestHostProcessStartAsync” + + + + Produce a TRX report for the current test session + 为当前测试会话生成 TRX 报表 + + + + TRX report generator + TRX 报表生成器 + + + + Required environment variable '{0}' is missing for TRX report generator + TRX 报表生成器缺少所需的环境变量“{0}” + + + + '--report-trx' cannot be enabled when using '--list-tests' + 使用“--list-tests”时,无法启用“--report-trx” + + + + Enable generating TRX report + 启用生成 TRX 报表 + + + + Requests of type '{0}' is not supported + 不支持类型为“{0}”的请求 + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hant.xlf new file mode 100644 index 0000000000..e599551f93 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Resources/xlf/ExtensionResources.zh-Hant.xlf @@ -0,0 +1,112 @@ + + + + + + TRX report generator only works with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' + TRX 報表產生器只能與類型為 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder' 的建立器一起運作 + + + + The baseline TRX file + 比較基準 TRX 檔案 + + + + '--{0}' and '--{1}' must both be specified + 必須同時指定 '--{0}' 和 '--{1}' + + + + This tool allows to compare and highights differences between 2 TRX reports + 此工具可讓您比較 2 個 TRX 報表之間的高差異 + + + + TRX comparer tool + TRX 比較工具 + + + + '--{0}' expects a single trx file path as argument + '--{0}' 需要單一 TRX 檔案路徑做為引數 + + + + The TRX file to compare with the baseline + 要與基準比較的 TRX 檔案 + + + + Test session + 測試工作階段 + + + + TRX Report + TRX 報表 + + + + '--report-trx-filename' file name argument must end with '.trx' (e.g. --report-trx-filename myreport.trx) + '--report-trx-filename' 檔案名稱引數的結尾必須是 '.trx' (例如 --report-trx-filename myreport.trx) + + + + The name of the generated TRX report + 產生的 TRX 報表名稱 + + + + '--report-trx-filename' requires '--report-trx' to be enabled + '--report-trx-filename' 需要啟用 '--report-trx' + + + + file name argument must not contain path (e.g. --report-trx-filename myreport.trx) + 檔案名稱引數不能包含路徑 (例如 --report-trx-filename myreport.trx) + + + + The test framework '{0}' with UID '{1}' does not support the 'ITrxReportCapability' leading to missing or incomplete TRX reports + 具有 UID '{1}' 的測試架構 '{0}' 不支援導致 TRX 報告遺漏或不完整的 'ITrxReportCapability' + + + + Method 'BeforeTestHostProcessStartAsync' must be called before 'OnTestHostProcessStartedAsync' + 'BeforeTestHostProcessStartAsync' 方法必須在 'OnTestHostProcessStartedAsync' 之前呼叫 + + + + Produce a TRX report for the current test session + 產生目前測試工作階段的 TRX 報告 + + + + TRX report generator + TRX 報表產生器 + + + + Required environment variable '{0}' is missing for TRX report generator + TRX 報表產生器遺漏必要的環境變數 '{0}' + + + + '--report-trx' cannot be enabled when using '--list-tests' + 使用 '--list-tests' 時,無法啟用 '--report-trx' + + + + Enable generating TRX report + 啟用產生 TRX 報表 + + + + Requests of type '{0}' is not supported + 不支援類型 '{0}' 的要求 + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/ReportFileNameSerializer.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/ReportFileNameSerializer.cs new file mode 100644 index 0000000000..a2102ed28d --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/ReportFileNameSerializer.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions.Serializers; + +internal sealed class ReportFileNameRequest(string fileName) : IRequest +{ + public string FileName { get; } = fileName; +} + +internal class ReportFileNameRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 1; + + public object Deserialize(Stream stream) + { + string reportFileName = ReadString(stream); + return new ReportFileNameRequest(reportFileName); + } + + public void Serialize(object objectToSerialize, Stream stream) + { + var reportFileName = (ReportFileNameRequest)objectToSerialize; + WriteString(stream, reportFileName.FileName); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/TestAdapterInformationsSerializer.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/TestAdapterInformationsSerializer.cs new file mode 100644 index 0000000000..7c3c41e2f0 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/TestAdapterInformationsSerializer.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions.Serializers; + +internal class TestAdapterInformationRequest : IRequest +{ + public TestAdapterInformationRequest(string testAdapterId, string testAdapterVersion) + { + TestAdapterId = testAdapterId; + TestAdapterVersion = testAdapterVersion; + } + + public string TestAdapterId { get; } + + public string TestAdapterVersion { get; } +} + +internal class TestAdapterInformationRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 2; + + public object Deserialize(Stream stream) + { + string testAdapterId = ReadString(stream); + string testAdapterVersion = ReadString(stream); + return new TestAdapterInformationRequest(testAdapterId, testAdapterVersion); + } + + public void Serialize(object objectToSerialize, Stream stream) + { + var testAdapterInformationRequest = (TestAdapterInformationRequest)objectToSerialize; + WriteString(stream, testAdapterInformationRequest.TestAdapterId); + WriteString(stream, testAdapterInformationRequest.TestAdapterVersion); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TestingPlatformBuilderHook.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TestingPlatformBuilderHook.cs new file mode 100644 index 0000000000..8ac8e958db --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TestingPlatformBuilderHook.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Builder; + +namespace Microsoft.Testing.Extensions.TrxReport; + +public static class TestingPlatformBuilderHook +{ + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _) + => testApplicationBuilder.AddTrxReportProvider(); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/ToolTrxCompareFactory.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/ToolTrxCompareFactory.cs new file mode 100644 index 0000000000..e88cb5b300 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/ToolTrxCompareFactory.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.TestReports.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.OutputDevice; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; + +internal sealed class ToolTrxCompareFactory : IExtension +{ + /// + public string Uid { get; } = nameof(TrxCompareTool); + + /// + public string Version { get; } = AppVersion.DefaultSemVer; + + /// + public string DisplayName { get; } = ExtensionResources.TrxComparerToolDisplayName; + + /// + public string Description { get; } = ExtensionResources.TrxComparerToolDescription; + + /// + public Task IsEnabledAsync() => Task.FromResult(true); + + public TrxCompareTool CreateTrxCompareTool(ICommandLineOptions commandLineOptions, IOutputDevice outputDisplay, ITask task) + => new(commandLineOptions, this, outputDisplay, task); + + public TrxCompareToolCommandLine CreateTrxCompareToolCommandLine() + => new(this); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCommandLine.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCommandLine.cs new file mode 100644 index 0000000000..51f36f19dc --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCommandLine.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.TestReports.Resources; +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; + +internal sealed class TrxReportGeneratorCommandLine : ICommandLineOptionsProvider +{ + public const string TrxReportOptionName = "report-trx"; + public const string TrxReportFileNameOptionName = "report-trx-filename"; + + /// + public string Uid { get; } = nameof(TrxReportGeneratorCommandLine); + + /// + public string Version { get; } = AppVersion.DefaultSemVer; + + /// + public string DisplayName { get; } = ExtensionResources.TrxReportGeneratorDisplayName; + + /// + public string Description { get; } = ExtensionResources.TrxReportGeneratorDescription; + + /// + public Task IsEnabledAsync() => Task.FromResult(true); + + public IReadOnlyCollection GetCommandLineOptions() + => + [ + new(TrxReportOptionName, ExtensionResources.TrxReportOptionDescription, ArgumentArity.Zero, false), + new(TrxReportFileNameOptionName, ExtensionResources.TrxReportFileNameOptionDescription, ArgumentArity.ExactlyOne, false) + ]; + + public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) + { + if (commandOption.Name == TrxReportFileNameOptionName) + { + if (!arguments[0].EndsWith(".trx", StringComparison.OrdinalIgnoreCase)) + { + return ValidationResult.InvalidTask(ExtensionResources.TrxReportFileNameExtensionIsNotTrx); + } + + if (!RoslynString.IsNullOrEmpty(Path.GetDirectoryName(arguments[0]))) + { + return ValidationResult.InvalidTask(ExtensionResources.TrxReportFileNameShouldNotContainPath); + } + } + + return ValidationResult.ValidTask; + } + + public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) + { + if (commandLineOptions.IsOptionSet(TrxReportFileNameOptionName) + && !commandLineOptions.IsOptionSet(TrxReportOptionName)) + { + return ValidationResult.InvalidTask(ExtensionResources.TrxReportFileNameRequiresTrxReport); + } + + if (commandLineOptions.IsOptionSet(TrxReportOptionName) + && commandLineOptions.IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey)) + { + return ValidationResult.InvalidTask(ExtensionResources.TrxReportIsNotValidForDiscovery); + } + + // No problem found + return ValidationResult.ValidTask; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.CommandLine.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.CommandLine.cs new file mode 100644 index 0000000000..5738762730 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.CommandLine.cs @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.TestReports.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Tools; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; + +internal class TrxCompareToolCommandLine : IToolCommandLineOptionsProvider +{ + public const string BaselineTrxOptionName = "baseline-trx"; + public const string TrxToCompareOptionName = "trx-to-compare"; + private readonly IExtension _extension; + + public TrxCompareToolCommandLine(IExtension extension) + { + _extension = extension; + Uid = _extension.Uid; + Version = _extension.Version; + DisplayName = _extension.DisplayName; + Description = _extension.Description; + } + + /// + public string Uid { get; } + + /// + public string Version { get; } = AppVersion.DefaultSemVer; + + /// + public string DisplayName { get; } + + /// + public string Description { get; } + + /// + public string ToolName { get; } = TrxCompareTool.ToolName; + + /// + public Task IsEnabledAsync() => Task.FromResult(true); + + public IReadOnlyCollection GetCommandLineOptions() + => + [ + new(BaselineTrxOptionName, ExtensionResources.TrxComparerToolBaselineFileOptionDescription, ArgumentArity.ExactlyOne, false), + new(TrxToCompareOptionName, ExtensionResources.TrxComparerToolOtherFileOptionDescription, ArgumentArity.ExactlyOne, false) + ]; + + public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) + { + if (commandOption.Name == BaselineTrxOptionName && (!arguments[0].EndsWith(".trx", StringComparison.OrdinalIgnoreCase) || !File.Exists(arguments[0]))) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxComparerToolOptionExpectsSingleArgument, BaselineTrxOptionName)); + } + + if (commandOption.Name == TrxToCompareOptionName && (!arguments[0].EndsWith(".trx", StringComparison.OrdinalIgnoreCase) || !File.Exists(arguments[0]))) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxComparerToolOptionExpectsSingleArgument, TrxToCompareOptionName)); + } + + // No problem found + return ValidationResult.ValidTask; + } + + public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) + { + if ((commandLineOptions.IsOptionSet(BaselineTrxOptionName) && !commandLineOptions.IsOptionSet(TrxToCompareOptionName)) + || (!commandLineOptions.IsOptionSet(BaselineTrxOptionName) && commandLineOptions.IsOptionSet(TrxToCompareOptionName))) + { + return Task.FromResult( + ValidationResult.Invalid( + string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxComparerToolBothFilesMustBeSpecified, BaselineTrxOptionName, TrxToCompareOptionName))); + } + + // No problem found + return ValidationResult.ValidTask; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.cs new file mode 100644 index 0000000000..54954ce08f --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.cs @@ -0,0 +1,250 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; +using System.Text; +using System.Xml.Linq; + +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Tools; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; + +internal class TrxCompareTool : ITool, IOutputDeviceDataProducer +{ + public const string ToolName = "ms-trxcompare"; + private readonly ICommandLineOptions _commandLineOptions; + private readonly IExtension _extension; + private readonly IOutputDevice _outputDisplay; + private readonly ITask _task; + + public TrxCompareTool(ICommandLineOptions commandLineOptions, IExtension extension, IOutputDevice outputDisplay, + ITask task) + { + _commandLineOptions = commandLineOptions; + _extension = extension; + _outputDisplay = outputDisplay; + _task = task; + } + + /// + public string Name { get; } = ToolName; + + /// + public bool Hidden { get; } + + /// + public string Uid => _extension.Uid; + + /// + public string Version => _extension.Version; + + /// + public string DisplayName => _extension.DisplayName; + + /// + public string Description => _extension.Description; + + /// + public Task IsEnabledAsync() => Task.FromResult(true); + + public async Task RunAsync() + { + if (!_commandLineOptions.TryGetOptionArgumentList(TrxCompareToolCommandLine.BaselineTrxOptionName, out string[]? baselineFilePaths) + || !_commandLineOptions.TryGetOptionArgumentList(TrxCompareToolCommandLine.TrxToCompareOptionName, out string[]? comparedFilePaths)) + { + throw ApplicationStateGuard.Unreachable(); + } + + XNamespace trxNamespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010"; + + List<(string TestName, string Outcome, string Storage)> baseLineResults = new(); + List baseLineIssues = new(); + List<(string TestName, string Outcome, string Storage)> comparedResults = new(); + List comparedIssues = new(); + await _task.WhenAll( + _task.Run(() => CollectEntriesAndErrors(baselineFilePaths[0], trxNamespace, baseLineResults, baseLineIssues)), + _task.Run(() => CollectEntriesAndErrors(comparedFilePaths[0], trxNamespace, comparedResults, comparedIssues))); + + StringBuilder outputBuilder = new(); + AppendResultsAndIssues("Baseline", baselineFilePaths[0], baseLineResults, baseLineIssues, outputBuilder); + AppendResultsAndIssues("Compared TRX", comparedFilePaths[0], comparedResults, comparedIssues, outputBuilder); + + if (AreMatchingTrxFiles(baseLineResults, comparedResults, outputBuilder)) + { + await _outputDisplay.DisplayAsync(this, new TextOutputDeviceData(outputBuilder.ToString())); + return ExitCodes.Success; + } + else + { + await _outputDisplay.DisplayAsync(this, new TextOutputDeviceData(outputBuilder.ToString())); + return ExitCodes.GenericFailure; + } + } + + private static bool AreMatchingTrxFiles( + List<(string TestName, string Outcome, string Storage)> baseLineResults, + List<(string TestName, string Outcome, string Storage)> comparedResults, + StringBuilder outputBuilder) + { + bool checkFailed = false; + outputBuilder.AppendLine("--- Comparing TRX files ---"); + + IEnumerable<((string TestName, string Outcome, string Storage), string Source)> trxEntries = + baseLineResults.Select(tuple => (tuple, "baseline")) + .Concat(comparedResults.Select(tuple => (tuple, "other"))) + .OrderBy(x => x.tuple.TestName); + + foreach (((string TestName, string Outcome, string Storage) sourceTrx, string entrySource) in trxEntries) + { + string otherSource = entrySource == "baseline" ? "other" : "baseline"; + IEnumerable<(string MatchingTestName, string MatchingOutcome, string MatchingStorage)> matchingEntries = + entrySource == "baseline" + ? comparedResults.Where(x => x.TestName == sourceTrx.TestName) + : baseLineResults.Where(x => x.TestName == sourceTrx.TestName); + if (!matchingEntries.Any()) + { + checkFailed = true; + outputBuilder.AppendLine( + CultureInfo.InvariantCulture, + $" - Test '{sourceTrx.TestName}' is missing inside the trx '{otherSource}'"); + continue; + } + + if (matchingEntries.Skip(1).Any()) + { + checkFailed = true; + outputBuilder.AppendLine( + CultureInfo.InvariantCulture, + $" - Test '{sourceTrx.TestName}' is found multiple times inside the trx '{otherSource}'"); + continue; + } + + (string otherTestName, string otherOutcome, string otherStorage) = matchingEntries.First(); + if (sourceTrx.Outcome != otherOutcome) + { + checkFailed = true; + outputBuilder.AppendLine( + CultureInfo.InvariantCulture, + $" - Test '{sourceTrx.TestName}' has a different outcome. Got '{otherOutcome}', expected '{sourceTrx.Outcome}'"); + } + } + + outputBuilder.AppendLine(); + if (checkFailed) + { + outputBuilder.AppendLine("Comparison check failed!"); + } + else + { + outputBuilder.AppendLine("Comparison check succeeded!"); + } + + return !checkFailed; + } + + private static void AppendResultsAndIssues(string category, string filePath, + List<(string TestName, string Outcome, string Storage)> results, List issues, StringBuilder outputBuilder) + { + outputBuilder.AppendLine(CultureInfo.InvariantCulture, $"--- {category} ---"); + outputBuilder.AppendLine(CultureInfo.InvariantCulture, $"File '{filePath}'"); + + outputBuilder.AppendLine("Issues:"); + foreach (string issue in issues) + { + outputBuilder.AppendLine(CultureInfo.InvariantCulture, $" - {issue}"); + } + + if (issues.Count == 0) + { + outputBuilder.AppendLine(" None"); + } + + outputBuilder.AppendLine(); + outputBuilder.AppendLine("Test containers (assemblies):"); + foreach (string? storage in results.Select(x => x.Storage).Distinct()) + { + outputBuilder.AppendLine(CultureInfo.InvariantCulture, $" - {storage}"); + } + + outputBuilder.AppendLine(); + outputBuilder.AppendLine("Test results:"); + foreach ((string outcome, int resultCount) in results.GroupBy(x => x.Outcome, (key, results) => (key, results.Count()))) + { + outputBuilder.AppendLine(CultureInfo.InvariantCulture, $" - {outcome}: {resultCount}"); + } + + outputBuilder.AppendLine(); + } + + private static void CollectEntriesAndErrors(string trxFile, XNamespace ns, List<(string TestName, string Outcome, string Storage)> results, List issues) + { + var trxTestRun = XElement.Parse(File.ReadAllText(trxFile)); + int testResultIndex = 0; + foreach (XElement testResult in trxTestRun.Elements(ns + "Results").Elements(ns + "UnitTestResult")) + { + testResultIndex++; + string? testId = testResult.Attribute("testId")?.Value; + if (testId is null) + { + issues.Add($"UnitTestResult at index '{testResultIndex}' is missing 'testId' attribute."); + continue; + } + + string? testResultTestName = testResult.Attribute("testName")?.Value; + if (testResultTestName is null) + { + issues.Add($"UnitTestResult at index '{testResultIndex}' is missing 'testName' attribute."); + continue; + } + + string? testResultOutcome = testResult.Attribute("outcome")?.Value; + if (testResultOutcome is null) + { + issues.Add($"UnitTestResult at index '{testResultIndex}' is missing 'outcome' attribute."); + continue; + } + + IEnumerable matchingUnitTestDefinitions = trxTestRun + .Elements(ns + "TestDefinitions") + .Elements(ns + "UnitTest") + .Where(x => x.Attribute("id")?.Value == testId); + if (matchingUnitTestDefinitions.Skip(1).Any()) + { + issues.Add($"Found more than one entry in 'TestDefinitions.UnitTest' matching the test ID '{testId}'."); + continue; + } + + if (!matchingUnitTestDefinitions.Any()) + { + issues.Add($"Cannot find any 'TestDefinitions.UnitTest' matching the test ID '{testId}'."); + continue; + } + + XElement matchingUnitTestDefinition = matchingUnitTestDefinitions.First(); + string testDefinitionId = matchingUnitTestDefinition.Attribute("id")!.Value; + + string? testDefinitionStorage = matchingUnitTestDefinition.Attribute("storage")?.Value; + if (testDefinitionStorage is null) + { + issues.Add($"Cannot find attribute 'storage' for 'TestDefinitions.UnitTest' with ID '{testDefinitionId}'."); + continue; + } + + string? testDefinitionClassName = matchingUnitTestDefinition.Element(ns + "TestMethod") + ?.Attribute("className") + ?.Value; + if (testDefinitionClassName is null) + { + issues.Add($"Cannot find attribute 'className' on sub node 'TestMethod' for 'TestDefinitions.UnitTest' with ID '{testDefinitionId}'."); + continue; + } + + results.Add((testDefinitionClassName + "." + testResultTestName, testResultOutcome, testDefinitionStorage)); + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs new file mode 100644 index 0000000000..7e82970344 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs @@ -0,0 +1,258 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.TestReports.Resources; +using Microsoft.Testing.Extensions.TrxReport.Abstractions.Serializers; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC.Models; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Services; +using Microsoft.Testing.Platform.TestHost; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; + +internal sealed class TrxReportGenerator : + IDataConsumer, + ITestSessionLifetimeHandler, + IDataProducer, + IOutputDeviceDataProducer +{ + private readonly IConfiguration _configuration; + private readonly ICommandLineOptions _commandLineOptionsService; + private readonly ITestApplicationModuleInfo _testApplicationModuleInfo; + private readonly IMessageBus _messageBus; + private readonly IClock _clock; + private readonly IEnvironment _environment; + private readonly IOutputDevice _outputDisplay; + private readonly ITestFramework _testFramework; + private readonly ITestFrameworkCapabilities _testFrameworkCapabilities; + private readonly TrxReportGeneratorCommandLine _trxReportGeneratorCommandLine; + private readonly TrxTestApplicationLifecycleCallbacks? _trxTestApplicationLifecycleCallbacks; + private readonly ILogger _logger; + private readonly List _tests = []; + private readonly Dictionary> _artifactsByTestNode = new(); + private readonly Dictionary> _artifactsByExtension = new(); + private readonly bool _isEnabled; + + private DateTimeOffset? _testStartTime; + private int _failedTestsCount; + private int _passedTestsCount; + private bool _adapterSupportTrxCapability; + + public TrxReportGenerator( + IConfiguration configuration, + ICommandLineOptions commandLineOptionsService, + ITestApplicationModuleInfo testApplicationModuleInfo, + IMessageBus messageBus, + IClock clock, + IEnvironment environment, + IOutputDevice outputDisplay, + ITestFramework testFramework, + ITestFrameworkCapabilities testFrameworkCapabilities, + TrxReportGeneratorCommandLine trxReportGeneratorCommandLine, + // Can be null in case of server mode + TrxTestApplicationLifecycleCallbacks? trxTestApplicationLifecycleCallbacks, + ILogger logger) + { + _configuration = configuration; + _commandLineOptionsService = commandLineOptionsService; + _testApplicationModuleInfo = testApplicationModuleInfo; + _messageBus = messageBus; + _clock = clock; + _environment = environment; + _outputDisplay = outputDisplay; + _testFramework = testFramework; + _testFrameworkCapabilities = testFrameworkCapabilities; + _trxReportGeneratorCommandLine = trxReportGeneratorCommandLine; + _trxTestApplicationLifecycleCallbacks = trxTestApplicationLifecycleCallbacks; + _logger = logger; + _isEnabled = commandLineOptionsService.IsOptionSet(TrxReportGeneratorCommandLine.TrxReportOptionName); + } + + public Type[] DataTypesConsumed { get; } = + [ + typeof(TestNodeUpdateMessage), + typeof(TestNodeFileArtifact), + typeof(SessionFileArtifact) + ]; + + public Type[] DataTypesProduced { get; } = [typeof(SessionFileArtifact)]; + + /// + public string Uid { get; } = nameof(TrxReportGenerator); + + /// + public string Version { get; } = AppVersion.DefaultSemVer; + + /// + public string DisplayName { get; } = ExtensionResources.TrxReportGeneratorDisplayName; + + /// + public string Description { get; } = ExtensionResources.TrxReportGeneratorDescription; + + /// + public Task IsEnabledAsync() => Task.FromResult(_isEnabled); + + public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) + { + if (!_isEnabled || cancellationToken.IsCancellationRequested) + { + return Task.CompletedTask; + } + + try + { + switch (value) + { + case TestNodeUpdateMessage nodeChangedMessage: + TestNodeStateProperty nodeState = nodeChangedMessage.TestNode.Properties.Single(); + if (nodeState is PassedTestNodeStateProperty) + { + _tests.Add(nodeChangedMessage); + _passedTestsCount++; + } + else if (Array.IndexOf(TestNodePropertiesCategories.WellKnownTestNodeTestRunOutcomeFailedProperties, nodeState.GetType()) != -1) + { + _tests.Add(nodeChangedMessage); + _failedTestsCount++; + } + else if (nodeState is SkippedTestNodeStateProperty) + { + _tests.Add(nodeChangedMessage); + } + + break; + case TestNodeFileArtifact testNodeFileArtifact: + if (!_artifactsByTestNode.TryGetValue(testNodeFileArtifact.TestNode.Uid, out List? nodeFileArtifacts)) + { + nodeFileArtifacts = [testNodeFileArtifact]; + _artifactsByTestNode[testNodeFileArtifact.TestNode.Uid] = nodeFileArtifacts; + } + else + { + nodeFileArtifacts.Add(testNodeFileArtifact); + } + + break; + case SessionFileArtifact fileArtifact: + if (!_artifactsByExtension.TryGetValue(dataProducer, out List? sessionFileArtifacts)) + { + sessionFileArtifacts = [fileArtifact]; + _artifactsByExtension[dataProducer] = sessionFileArtifacts; + } + else + { + sessionFileArtifacts.Add(fileArtifact); + } + + break; + } + } + catch (OperationCanceledException ex) when (ex.CancellationToken == cancellationToken) + { + // Do nothing, we're stopping + } + + return Task.CompletedTask; + } + + public async Task OnTestSessionStartingAsync(SessionUid _, CancellationToken cancellationToken) + { + if (!_isEnabled || cancellationToken.IsCancellationRequested) + { + return; + } + + if (_logger.IsEnabled(LogLevel.Debug)) + { + await _logger.LogDebugAsync($""" +PlatformCommandLineProvider.ServerOption: {_commandLineOptionsService.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey)} +CrashDumpCommandLineOptions.CrashDumpOptionName: {_commandLineOptionsService.IsOptionSet(CrashDumpCommandLineOptions.CrashDumpOptionName)} +TrxReportGeneratorCommandLine.IsTrxReportEnabled: {_commandLineOptionsService.IsOptionSet(TrxReportGeneratorCommandLine.TrxReportOptionName)} +"""); + } + + if (!_commandLineOptionsService.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey) && + _commandLineOptionsService.IsOptionSet(CrashDumpCommandLineOptions.CrashDumpOptionName)) + { + ApplicationStateGuard.Ensure(_trxTestApplicationLifecycleCallbacks is not null); + ApplicationStateGuard.Ensure(_trxTestApplicationLifecycleCallbacks.NamedPipeClient is not null); + + try + { + await _trxTestApplicationLifecycleCallbacks.NamedPipeClient.RequestReplyAsync(new TestAdapterInformationRequest(_testFramework.Uid, _testFramework.Version), cancellationToken) + .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + } + catch (OperationCanceledException ex) when (ex.CancellationToken == cancellationToken) + { + // Do nothing, we're stopping + } + } + + ITrxReportCapability? trxCapability = _testFrameworkCapabilities.GetCapability(); + if (_isEnabled && trxCapability is not null && trxCapability?.IsSupported == true) + { + _adapterSupportTrxCapability = true; + trxCapability.Enable(); + } + + _testStartTime = _clock.UtcNow; + } + + public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, CancellationToken cancellationToken) + { + if (!_isEnabled || cancellationToken.IsCancellationRequested) + { + return; + } + + try + { + if (!_adapterSupportTrxCapability) + { + await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateYellowConsoleColorText(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxReportFrameworkDoesNotSupportTrxReportCapability, _testFramework.DisplayName, _testFramework.Uid))); + } + + ApplicationStateGuard.Ensure(_testStartTime is not null); + + TrxReportEngine trxReportGeneratorEngine = new(_testApplicationModuleInfo, _environment, _commandLineOptionsService, _configuration, + _clock, _tests.ToArray(), _failedTestsCount, _passedTestsCount, _artifactsByExtension, _artifactsByTestNode, + _adapterSupportTrxCapability, _testFramework, _testStartTime.Value, cancellationToken); + string reportFileName = await trxReportGeneratorEngine.GenerateReportAsync(); + + if ( + + // TestController is not used when we run in server mode + _commandLineOptionsService.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey) || + + // If crash dump is not enabled we run trx in-process only + !_commandLineOptionsService.IsOptionSet(CrashDumpCommandLineOptions.CrashDumpOptionName)) + { + // In server mode we report the trx in-process + await _messageBus.PublishAsync(this, new SessionFileArtifact(sessionUid, new FileInfo(reportFileName), ExtensionResources.TrxReportArtifactDisplayName, ExtensionResources.TrxReportArtifactDescription)); + } + else + { + ApplicationStateGuard.Ensure(_trxTestApplicationLifecycleCallbacks is not null); + ApplicationStateGuard.Ensure(_trxTestApplicationLifecycleCallbacks.NamedPipeClient is not null); + await _trxTestApplicationLifecycleCallbacks.NamedPipeClient.RequestReplyAsync(new ReportFileNameRequest(reportFileName), cancellationToken); + } + } + catch (OperationCanceledException ex) when (ex.CancellationToken == cancellationToken) + { + // Do nothing, we're stopping + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxEnvironmentVariableProvider.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxEnvironmentVariableProvider.cs new file mode 100644 index 0000000000..d19aa3f54e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxEnvironmentVariableProvider.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.TestReports.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.TestHostControllers; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; + +internal sealed class TrxEnvironmentVariableProvider : ITestHostEnvironmentVariableProvider +{ + public const string TRXNAMEDPIPENAME = nameof(TRXNAMEDPIPENAME); + + private readonly ICommandLineOptions _commandLineOptions; + private readonly string _pipeName; + + public TrxEnvironmentVariableProvider(ICommandLineOptions commandLineOptions, string pipeName) + { + _commandLineOptions = commandLineOptions; + _pipeName = pipeName; + } + + public string Uid => nameof(TrxEnvironmentVariableProvider); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => ExtensionResources.TrxReportGeneratorDisplayName; + + public string Description => ExtensionResources.TrxReportGeneratorDescription; + + public Task IsEnabledAsync() +#pragma warning disable SA1114 // Parameter list should follow declaration + => Task.FromResult( + // TrxReportGenerator is enabled only when trx report is enabled + _commandLineOptions.IsOptionSet(TrxReportGeneratorCommandLine.TrxReportOptionName) + // TestController is not used when we run in server mode + && !_commandLineOptions.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey) + // If crash dump is not enabled we run trx in-process only + && _commandLineOptions.IsOptionSet(CrashDumpCommandLineOptions.CrashDumpOptionName)); +#pragma warning restore SA1114 // Parameter list should follow declaration + + public Task UpdateAsync(IEnvironmentVariables environmentVariables) + { + environmentVariables.SetVariable(new(TRXNAMEDPIPENAME, _pipeName, false, true)); + return Task.CompletedTask; + } + + public Task ValidateTestHostEnvironmentVariablesAsync(IReadOnlyEnvironmentVariables environmentVariables) + { + if (!environmentVariables.TryGetVariable(TRXNAMEDPIPENAME, out _)) + { + return Task.FromResult( + ValidationResult.Invalid( + string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxReportGeneratorMissingTrxNamedPipeEnvironmentVariable, TRXNAMEDPIPENAME))); + } + + // No problem found + return ValidationResult.ValidTask; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs new file mode 100644 index 0000000000..9d9b8d38f9 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs @@ -0,0 +1,289 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.TestReports.Resources; +using Microsoft.Testing.Extensions.TrxReport.Abstractions.Serializers; +using Microsoft.Testing.Platform.Capabilities; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.Extensions.TestHostControllers; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Models; +using Microsoft.Testing.Platform.IPC.Serializers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.Services; +using Microsoft.Testing.Platform.TestHost; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; + +internal sealed class TrxProcessLifetimeHandler : + ITestHostProcessLifetimeHandler, + IDataConsumer, + IDataProducer, +#if NETCOREAPP + IAsyncDisposable +#else + IDisposable +#endif +{ + private readonly ICommandLineOptions _commandLineOptions; + private readonly IEnvironment _environment; + private readonly IMessageBus _messageBus; + private readonly ITestApplicationModuleInfo _testApplicationModuleInfo; + private readonly IConfiguration _configuration; + private readonly IClock _clock; + private readonly ITask _task; + private readonly ILogger _logger; + private readonly PipeNameDescription _pipeNameDescription; + private readonly Dictionary> _fileArtifacts = new(); + private readonly DateTimeOffset _startTime; + + private NamedPipeServer? _singleConnectionNamedPipeServer; + private Task? _waitConnectionTask; + private ReportFileNameRequest? _fileNameRequest; + private TestAdapterInformationRequest? _testAdapterInformationRequest; + + public TrxProcessLifetimeHandler( + ICommandLineOptions commandLineOptions, + IEnvironment environment, + ILoggerFactory loggerFactory, + IMessageBus messageBus, + ITestApplicationModuleInfo testApplicationModuleInfo, + IConfiguration configuration, + IClock clock, + ITask task, + PipeNameDescription pipeNameDescription) + { + _commandLineOptions = commandLineOptions; + _environment = environment; + _messageBus = messageBus; + _testApplicationModuleInfo = testApplicationModuleInfo; + _configuration = configuration; + _clock = clock; + _task = task; + _pipeNameDescription = pipeNameDescription; + _logger = loggerFactory.CreateLogger(); + _startTime = _clock.UtcNow; + } + + public string Uid => nameof(TrxProcessLifetimeHandler); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => string.Empty; + + public string Description => string.Empty; + + public Type[] DataTypesConsumed => [typeof(FileArtifact)]; + + public Type[] DataTypesProduced => [typeof(FileArtifact)]; + + public Task IsEnabledAsync() +#pragma warning disable SA1114 // Parameter list should follow declaration + => Task.FromResult( + // TrxReportGenerator is enabled only when trx report is enabled + _commandLineOptions.IsOptionSet(TrxReportGeneratorCommandLine.TrxReportOptionName) + // TestController is not used when we run in server mode + && !_commandLineOptions.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey) + // If crash dump is not enabled we run trx in-process only + && _commandLineOptions.IsOptionSet(CrashDumpCommandLineOptions.CrashDumpOptionName)); +#pragma warning restore SA1114 // Parameter list should follow declaration + + public Task BeforeTestHostProcessStartAsync(CancellationToken cancellation) + { + _waitConnectionTask = _task.Run( + async () => + { + _singleConnectionNamedPipeServer = new(_pipeNameDescription, CallbackAsync, _environment, _logger, _task, cancellation); + _singleConnectionNamedPipeServer.RegisterSerializer(new ReportFileNameRequestSerializer(), typeof(ReportFileNameRequest)); + _singleConnectionNamedPipeServer.RegisterSerializer(new TestAdapterInformationRequestSerializer(), typeof(TestAdapterInformationRequest)); + _singleConnectionNamedPipeServer.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + await _singleConnectionNamedPipeServer.WaitConnectionAsync(cancellation).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + }, cancellation); + + return Task.CompletedTask; + } + + public async Task OnTestHostProcessStartedAsync(ITestHostProcessInformation testHostProcessInformation, CancellationToken cancellation) + { + if (_waitConnectionTask is null) + { + throw new InvalidOperationException(ExtensionResources.TrxReportGeneratorBeforeTestHostProcessStartAsyncNotCalled); + } + + await _waitConnectionTask.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellation); + } + + public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) + { + if (!_fileArtifacts.TryGetValue(dataProducer, out List? fileArtifacts)) + { + fileArtifacts = []; + _fileArtifacts.Add(dataProducer, fileArtifacts); + } + + fileArtifacts.Add((FileArtifact)value); + return Task.CompletedTask; + } + + public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testHostProcessInformation, CancellationToken cancellation) + { + if (cancellation.IsCancellationRequested) + { + return; + } + + Dictionary> artifacts = new(); + + foreach (KeyValuePair> prodArtifacts in _fileArtifacts) + { + var perProducerArtifact = new List(); + var extensionInfo = new ExtensionInfo(prodArtifacts.Key.Uid, prodArtifacts.Key.Version, prodArtifacts.Key.DisplayName, prodArtifacts.Key.Description); + foreach (FileArtifact fileArtifact in prodArtifacts.Value) + { + perProducerArtifact.Add(new SessionFileArtifact(new SessionUid(Guid.Empty.ToString()), fileArtifact.FileInfo, fileArtifact.DisplayName, fileArtifact.Description)); + } + + artifacts.Add(extensionInfo, perProducerArtifact); + } + + // We create a trx with only files in case of test host process crash. + if (!testHostProcessInformation.HasExitedGracefully) + { + TrxReportEngine trxReportGeneratorEngine = new(_testApplicationModuleInfo, _environment, _commandLineOptions, _configuration, + _clock, [], 0, 0, + artifacts, + new Dictionary>(), + adapterSupportTrxCapability: null, + new TestAdapterInfo(_testAdapterInformationRequest!.TestAdapterId, _testAdapterInformationRequest.TestAdapterVersion), + _startTime, + cancellation); + + await _messageBus.PublishAsync( + this, + new FileArtifact( + new FileInfo(await trxReportGeneratorEngine.GenerateReportAsync( + isTestHostCrashed: true, + testHostCrashInfo: $"Test host process pid: {testHostProcessInformation.PID} crashed.")), + ExtensionResources.TrxReportArtifactDisplayName, + ExtensionResources.TrxReportArtifactDescription)); + return; + } + + if (_fileNameRequest is null) + { + throw ApplicationStateGuard.Unreachable(); + } + + var trxFile = new FileInfo(_fileNameRequest.FileName); + + // Add attachments to the trx. + if (_fileArtifacts.Count > 0) + { + TrxReportEngine trxReportGeneratorEngine = new(_testApplicationModuleInfo, _environment, _commandLineOptions, _configuration, + _clock, [], 0, 0, + artifacts, + new Dictionary>(), + false, + new TestAdapterInfo(_testAdapterInformationRequest!.TestAdapterId, _testAdapterInformationRequest.TestAdapterVersion), + _startTime, + cancellation); + + await trxReportGeneratorEngine.AddArtifactsAsync(trxFile, artifacts); + } + + await _messageBus.PublishAsync(this, new FileArtifact(trxFile, ExtensionResources.TrxReportArtifactDisplayName, ExtensionResources.TrxReportArtifactDescription)); + } + + private Task CallbackAsync(IRequest request) + { + if (request is ReportFileNameRequest report) + { + _fileNameRequest = report; + return Task.FromResult((IResponse)VoidResponse.CachedInstance); + } + else if (request is TestAdapterInformationRequest testAdapterInformationRequest) + { + _testAdapterInformationRequest = testAdapterInformationRequest; + return Task.FromResult((IResponse)VoidResponse.CachedInstance); + } + else + { + throw new ArgumentOutOfRangeException(string.Format(CultureInfo.InvariantCulture, ExtensionResources.UnsupportedRequestTypeErrorMessage, request.GetType().FullName)); + } + } + +#if NETCOREAPP + public async ValueTask DisposeAsync() + { + await DisposeHelper.DisposeAsync(_singleConnectionNamedPipeServer); + + // Dispose the pipe descriptor after the server to ensure the pipe is closed. + _pipeNameDescription?.Dispose(); + } +#else + public void Dispose() + { + _singleConnectionNamedPipeServer?.Dispose(); + + // Dispose the pipe descriptor after the server to ensure the pipe is closed. + _pipeNameDescription?.Dispose(); + } +#endif + + private class ExtensionInfo : IExtension + { + public ExtensionInfo(string id, string semVer, string displayName, string description) + { + Uid = id; + Version = semVer; + DisplayName = displayName; + Description = description; + } + + public string Uid { get; } + + public string Version { get; } + + public string DisplayName { get; } + + public string Description { get; } + + public Task IsEnabledAsync() => throw new NotImplementedException(); + } + + private class TestAdapterInfo : ITestFramework + { + public TestAdapterInfo(string id, string semVer) + { + Uid = id; + Version = semVer; + } + + public string Uid { get; } + + public string Version { get; } + + public string DisplayName => throw new NotImplementedException(); + + public string Description => throw new NotImplementedException(); + + public ICapability[] Capabilities => throw new NotImplementedException(); + + public Task IsEnabledAsync() => throw new NotImplementedException(); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) => throw new NotImplementedException(); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) => throw new NotImplementedException(); + + public Task ExecuteRequestAsync(ExecuteRequestContext context) => throw new NotImplementedException(); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs new file mode 100644 index 0000000000..d80c658f8e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs @@ -0,0 +1,715 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if NETCOREAPP +using System.Buffers; +#endif +using System.Globalization; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml.Linq; + +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; + +internal sealed partial class TrxReportEngine +{ + private const string UnitTestTypeGuid = "13CDC9D9-DDB5-4fa4-A97D-D965CCFC6D4B"; + + private static readonly Regex ReservedFileNamesRegex = BuildReservedFileNameRegex(); + private static readonly Regex InvalidXmlCharReplace = BuildInvalidXmlCharReplace(); + private static readonly MatchEvaluator InvalidXmlEvaluator = ReplaceInvalidCharacterWithUniCodeEscapeSequence; + + private static readonly Type[] FailedStates = + [ + typeof(FailedTestNodeStateProperty), + typeof(CancelledTestNodeStateProperty), + typeof(ErrorTestNodeStateProperty), + typeof(TimeoutTestNodeStateProperty) + ]; + + private static readonly HashSet InvalidFileNameChars = + [ + '\"', + '<', + '>', + '|', + '\0', + (char)1, + (char)2, + (char)3, + (char)4, + (char)5, + (char)6, + (char)7, + (char)8, + (char)9, + (char)10, + (char)11, + (char)12, + (char)13, + (char)14, + (char)15, + (char)16, + (char)17, + (char)18, + (char)19, + (char)20, + (char)21, + (char)22, + (char)23, + (char)24, + (char)25, + (char)26, + (char)27, + (char)28, + (char)29, + (char)30, + (char)31, + ':', + '*', + '?', + '\\', + '/', + '@', + '(', + ')', + '^', + ' ' + ]; + + private readonly ITestApplicationModuleInfo _testApplicationModuleInfo; + private readonly IEnvironment _environment; + private readonly ICommandLineOptions _commandLineOptionsService; + private readonly IConfiguration _configuration; + private readonly IClock _clock; + private readonly TestNodeUpdateMessage[] _testNodeUpdatedMessages; + private readonly int _failedTestsCount; + private readonly int _passedTestsCount; + private readonly Dictionary> _artifactsByExtension; + private readonly Dictionary> _artifactsByTestNode; + private readonly bool? _adapterSupportTrxCapability; + private readonly ITestFramework _testFrameworkAdapter; + private readonly DateTimeOffset _testStartTime; + private readonly CancellationToken _cancellationToken; + private readonly XNamespace _namespaceUri = XNamespace.Get("http://microsoft.com/schemas/VisualStudio/TeamTest/2010"); + private readonly IFileSystem _fileSystem; + private readonly bool _isCopyingFileAllowed; + + public TrxReportEngine(ITestApplicationModuleInfo testApplicationModuleInfo, IEnvironment environment, ICommandLineOptions commandLineOptionsService, IConfiguration configuration, IClock clock, TestNodeUpdateMessage[] testNodeUpdatedMessages, int failedTestsCount, int passedTestsCount, Dictionary> artifactsByExtension, Dictionary> artifactsByTestNode, bool? adapterSupportTrxCapability, ITestFramework testFrameworkAdapter, DateTimeOffset testStartTime, CancellationToken cancellationToken) + : this( + new SystemFileSystem(), + testApplicationModuleInfo, + environment, + commandLineOptionsService, + configuration, + clock, + testNodeUpdatedMessages, + failedTestsCount, + passedTestsCount, + artifactsByExtension, + artifactsByTestNode, + adapterSupportTrxCapability, + testFrameworkAdapter, + testStartTime, + cancellationToken) + { + } + + internal TrxReportEngine(IFileSystem fileSystem, ITestApplicationModuleInfo testApplicationModuleInfo, IEnvironment environment, ICommandLineOptions commandLineOptionsService, IConfiguration configuration, IClock clock, TestNodeUpdateMessage[] testNodeUpdatedMessages, int failedTestsCount, int passedTestsCount, Dictionary> artifactsByExtension, Dictionary> artifactsByTestNode, bool? adapterSupportTrxCapability, ITestFramework testFrameworkAdapter, DateTimeOffset testStartTime, CancellationToken cancellationToken, bool isCopyingFileAllowed = true) + { + _testApplicationModuleInfo = testApplicationModuleInfo; + _environment = environment; + _commandLineOptionsService = commandLineOptionsService; + _configuration = configuration; + _clock = clock; + _testNodeUpdatedMessages = testNodeUpdatedMessages; + _failedTestsCount = failedTestsCount; + _passedTestsCount = passedTestsCount; + _artifactsByExtension = artifactsByExtension; + _artifactsByTestNode = artifactsByTestNode; + _adapterSupportTrxCapability = adapterSupportTrxCapability; + _testFrameworkAdapter = testFrameworkAdapter; + _testStartTime = testStartTime; + _cancellationToken = cancellationToken; + _fileSystem = fileSystem; + _isCopyingFileAllowed = isCopyingFileAllowed; + } + + public async Task GenerateReportAsync(string testHostCrashInfo = "", bool isTestHostCrashed = false, bool keepReportFileStreamOpen = false) + => await RetryWhenIOExceptionAsync(async () => + { + string testAppModule = _testApplicationModuleInfo.GetCurrentTestApplicationFullPath(); + + // create the xml doc + var document = new XDocument(new XDeclaration("1.0", "UTF-8", null)); + var testRun = new XElement(_namespaceUri + "TestRun"); + testRun.SetAttributeValue("id", Guid.NewGuid()); + string testRunName = $"{_environment.GetEnvironmentVariable("UserName")}@{_environment.MachineName} {FormatDateTimeForRunName(_clock.UtcNow)}"; + testRun.SetAttributeValue("name", testRunName); + + AddTimes(testRun); + + // If the user added the the trxFileName the runDeploymentRoot would stay the same, We think it's a bug but I found that same behavior on vstest + string runDeploymentRoot = AddTestSettings(testRun, testRunName); + string trxFileName = $"{runDeploymentRoot}.trx"; + if (_commandLineOptionsService.TryGetOptionArgumentList(TrxReportGeneratorCommandLine.TrxReportFileNameOptionName, out string[]? fileName)) + { + trxFileName = ReplaceInvalidFileNameChars(fileName[0]); + } + + AddResults(testAppModule, testRun, out XElement testDefinitions, out XElement testEntries, out string uncategorizedTestId, out string resultSummaryOutcome); + testRun.Add(testDefinitions); + testRun.Add(testEntries); + AddTestLists(testRun, uncategorizedTestId); + + // NotExecuted is the status for the skipped test. + resultSummaryOutcome = isTestHostCrashed ? "Failed" : resultSummaryOutcome is "Passed" or "NotExecuted" ? "Completed" : resultSummaryOutcome; + + await AddResultSummaryAsync(testRun, resultSummaryOutcome, runDeploymentRoot, testHostCrashInfo, isTestHostCrashed); + + // will need catch Unauthorized access + document.Add(testRun); + + foreach (XElement node in document.Root!.Descendants().Where(n => n.Name.NamespaceName == string.Empty)) + { + // Remove the xmlns='' attribute. Note the use of + // Attributes rather than Attribute, in case the + // attribute doesn't exist (which it might not if we'd + // created the document "manually" instead of loading + // it from a file.) + node.Attributes("xmlns").Remove(); + + // Inherit the parent namespace instead + node.Name = node.Parent!.Name.Namespace + node.Name.LocalName; + } + + string outputDirectory = _configuration.GetTestResultDirectory(); // add var for this + string finalFileName = Path.Combine(outputDirectory, trxFileName); + Stream stream = _fileSystem.NewFileStream(finalFileName, FileMode.CreateNew).Stream; + try + { +#if NETCOREAPP + await document.SaveAsync(stream, SaveOptions.None, _cancellationToken); + return finalFileName; +#else + _cancellationToken.ThrowIfCancellationRequested(); + document.Save(stream); + return await Task.FromResult(finalFileName); +#endif + } + finally + { + if (!keepReportFileStreamOpen) + { +#if NET + await stream.DisposeAsync(); +#else + stream.Dispose(); +#endif + } + } + }); + + private async Task RetryWhenIOExceptionAsync(Func> func) + { + DateTimeOffset firstTryTime = _clock.UtcNow; + bool throwIOException = false; + while (true) + { + try + { + return await func(); + } + catch (IOException) + { + // In case of file with the same name we retry with a new name. + if (throwIOException) + { + throw; + } + } + + // We try for 30 seconds to create a file with a unique name. + if (_clock.UtcNow - firstTryTime > TimeSpan.FromSeconds(30)) + { + throwIOException = true; + } + } + } + + public async Task AddArtifactsAsync(FileInfo trxFile, Dictionary> artifacts) + { + var document = XDocument.Load(trxFile.FullName); + XElement deployment = document.Element(_namespaceUri + "TestRun")?.Element(_namespaceUri + "TestSettings")?.Element(_namespaceUri + "Deployment") + ?? throw new InvalidOperationException("Deployment element not found"); + string? runDeploymentRoot = deployment.Attribute("runDeploymentRoot")?.Value + ?? throw new InvalidOperationException("Unexpected null 'runDeploymentRoot'"); + XElement? resultSummary = document.Element(_namespaceUri + "TestRun")?.Element(_namespaceUri + "ResultSummary") + ?? throw new InvalidOperationException("ResultSummary element not found"); + XElement? collectorDataEntries = resultSummary.Element(_namespaceUri + "CollectorDataEntries"); + if (collectorDataEntries is null) + { + collectorDataEntries = new XElement(_namespaceUri + "CollectorDataEntries"); + resultSummary.Add(collectorDataEntries); + } + + foreach (KeyValuePair> extensionArtifacts in artifacts) + { + var collector = new XElement( + _namespaceUri + "Collector", + new XAttribute("agentName", _environment.MachineName), + new XAttribute("uri", $"datacollector://{extensionArtifacts.Key.Uid}/{extensionArtifacts.Key.Version}"), + new XAttribute("collectorDisplayName", extensionArtifacts.Key.DisplayName)); + collectorDataEntries.Add(collector); + + var uriAttachments = new XElement(_namespaceUri + "UriAttachments"); + collector.Add(uriAttachments); + + foreach (SessionFileArtifact artifact in extensionArtifacts.Value) + { + string href = await CopyArtifactIntoTrxDirectoryAndReturnHrefValueAsync(artifact.FileInfo, runDeploymentRoot); + uriAttachments.Add(new XElement(_namespaceUri + "UriAttachment", new XElement(_namespaceUri + "A", new XAttribute("href", href)))); + } + } + +#if NETCOREAPP + using FileStream fs = File.OpenWrite(trxFile.FullName); + await document.SaveAsync(fs, SaveOptions.None, _cancellationToken); +#else + _cancellationToken.ThrowIfCancellationRequested(); + document.Save(trxFile.FullName); + await Task.CompletedTask; +#endif + } + + private async Task AddResultSummaryAsync(XElement testRun, string resultSummaryOutcome, string runDeploymentRoot, string testHostCrashInfo, bool isTestHostCrashed = false) + { + var resultSummary = new XElement( + _namespaceUri + "ResultSummary", + new XAttribute("outcome", resultSummaryOutcome)); + testRun.Add(resultSummary); + + var counters = new XElement( + _namespaceUri + "Counters", + new XAttribute("total", _testNodeUpdatedMessages.Length), + new XAttribute("executed", _passedTestsCount + _failedTestsCount), + new XAttribute("passed", _passedTestsCount), + new XAttribute("failed", _failedTestsCount), + new XAttribute("error", 0), + new XAttribute("timeout", 0), + new XAttribute("aborted", 0), + new XAttribute("inconclusive", 0), + new XAttribute("passedButRunAborted", 0), + new XAttribute("notRunnable", 0), + new XAttribute("notExecuted", 0), + new XAttribute("disconnected", 0), + new XAttribute("warning", 0), + new XAttribute("completed", 0), + new XAttribute("inProgress", 0), + new XAttribute("pending", 0)); + resultSummary.Add(counters); + + if (isTestHostCrashed) + { + var runInfos = new XElement(_namespaceUri + "RunInfos"); + resultSummary.Add(runInfos); + var runInfo = new XElement( + _namespaceUri + "RunInfo", + new XAttribute("computerName", _environment.MachineName), + new XAttribute("outcome", "Error"), + new XAttribute("timestamp", _clock.UtcNow.DateTime)); + var text = new XElement(_namespaceUri + "Text", testHostCrashInfo); + runInfo.Add(text); + runInfos.Add(runInfo); + } + + if (_artifactsByExtension.Count == 0) + { + return; + } + + var collectorDataEntries = new XElement(_namespaceUri + "CollectorDataEntries"); + resultSummary.Add(collectorDataEntries); + + foreach (KeyValuePair> tuple in _artifactsByExtension) + { + var collector = new XElement( + _namespaceUri + "Collector", + new XAttribute("agentName", _environment.MachineName), + new XAttribute("uri", $"datacollector://{tuple.Key.Uid}/{tuple.Key.Version}"), + new XAttribute("collectorDisplayName", tuple.Key.DisplayName)); + collectorDataEntries.Add(collector); + + var uriAttachments = new XElement(_namespaceUri + "UriAttachments"); + collector.Add(uriAttachments); + + foreach (SessionFileArtifact artifact in tuple.Value) + { + string href = await CopyArtifactIntoTrxDirectoryAndReturnHrefValueAsync(artifact.FileInfo, runDeploymentRoot); + uriAttachments.Add(new XElement(_namespaceUri + "UriAttachment", new XElement(_namespaceUri + "A", new XAttribute("href", href)))); + } + } + } + + private async Task CopyArtifactIntoTrxDirectoryAndReturnHrefValueAsync(FileInfo artifact, string runDeploymentRoot) + { + string artifactDirectory = CreateOrGetTrxArtifactDirectory(runDeploymentRoot); + string fileName = artifact.Name; + + string destination = Path.Combine(artifactDirectory, fileName); + int nameCounter = 0; + + // If the file already exists, append a number to the end of the file name + while (true) + { + if (File.Exists(destination)) + { + nameCounter++; + destination = Path.Combine(artifactDirectory, $"{Path.GetFileNameWithoutExtension(fileName)}_{nameCounter}{Path.GetExtension(fileName)}"); + continue; + } + + break; + } + + await CopyFileAsync(artifact, new FileInfo(destination)); + + return Path.Combine(_environment.MachineName, Path.GetFileName(destination)); + } + + private string CreateOrGetTrxArtifactDirectory(string runDeploymentRoot) + { + string directoryName = Path.Combine(_configuration.GetTestResultDirectory(), runDeploymentRoot, "In", _environment.MachineName); + if (!Directory.Exists(directoryName)) + { + Directory.CreateDirectory(directoryName); + } + + return directoryName; + } + + private async Task CopyFileAsync(FileInfo origin, FileInfo destination) + { + if (!_isCopyingFileAllowed) + { + return; + } + + using FileStream fileStream = File.OpenRead(origin.FullName); + using var destinationStream = new FileStream(destination.FullName, FileMode.Create); + await fileStream.CopyToAsync(destinationStream); + } + + private static void AddTestLists(XElement testRun, string uncategorizedTestId) + { + var testLists = new XElement( + "TestLists", + new XElement( + "TestList", + new XAttribute("name", "Results Not in a List"), + new XAttribute("id", uncategorizedTestId)), + new XElement( + "TestList", + new XAttribute("name", "All Loaded Results"), + // parent of all categories (fake, not real category). + new XAttribute("id", new Guid("19431567-8539-422a-85D7-44EE4E166BDA")))); + + testRun.Add(testLists); + } + + private void AddResults(string testAppModule, XElement testRun, out XElement testDefinitions, out XElement testEntries, out string uncategorizedTestId, out string resultSummaryOutcome) + { + var results = new XElement("Results"); + + // Duplicate test ids are not allowed inside the TestDefinitions element. + testDefinitions = new XElement("TestDefinitions"); + var uniqueTestDefinitionTestIds = new HashSet(); + + testEntries = new XElement("TestEntries"); + uncategorizedTestId = "8C84FA94-04C1-424b-9868-57A2D4851A1D"; + resultSummaryOutcome = "Passed"; + foreach (TestNodeUpdateMessage nodeMessage in _testNodeUpdatedMessages) + { + TestNode testNode = nodeMessage.TestNode; + + string id = GuidFromString($"{testNode.Uid.Value} {testNode.DisplayName}").ToString(); + string displayName = RemoveInvalidXmlChar(testNode.DisplayName)!; + string executionId = Guid.NewGuid().ToString(); + + // Results + var unitTestResult = new XElement( + "UnitTestResult", + new XAttribute("executionId", executionId), + new XAttribute("testId", id), + new XAttribute("testName", displayName), + new XAttribute("computerName", _environment.MachineName)); + + TimingProperty? timing = testNode.Properties.SingleOrDefault(); + string testDuration = timing?.GlobalTiming.Duration is { } duration + ? duration.ToString("hh\\:mm\\:ss\\.fffffff", CultureInfo.InvariantCulture) + : "00:00:00"; + unitTestResult.SetAttributeValue("duration", testDuration); + + unitTestResult.SetAttributeValue( + "startTime", + timing?.GlobalTiming.StartTime.ToUniversalTime().ToString("O") ?? _clock.UtcNow.ToString("O")); + unitTestResult.SetAttributeValue( + "endTime", + timing?.GlobalTiming.EndTime.ToUniversalTime().ToString("O") ?? _clock.UtcNow.ToString("O")); + + // TODO: Are there other types? + unitTestResult.SetAttributeValue("testType", UnitTestTypeGuid); + + string outcome = "Passed"; + TestNodeStateProperty? testState = testNode.Properties.SingleOrDefault(); + if (testState is { } state + && FailedStates.Contains(testState.GetType())) + { + outcome = resultSummaryOutcome = "Failed"; + } + else if (testState is SkippedTestNodeStateProperty) + { + outcome = resultSummaryOutcome = "NotExecuted"; + } + + unitTestResult.SetAttributeValue("outcome", outcome); + + unitTestResult.SetAttributeValue("testListId", uncategorizedTestId); + + // It has the same value as executionId + unitTestResult.SetAttributeValue("relativeResultsDirectory", executionId); + + // Below we're escaping most "dynamic body" using .Replace("\0", ""), because this is an invalid xml character. + // There're other invalid xml characters, but they're transformed inside the writer in a correct way so we try to + // rely on the built-in escaping/conversion. + // i.e. https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriter.cs#L890 + var output = new XElement("Output"); + + TrxMessagesProperty? trxMessages = testNode.Properties.SingleOrDefault(); + IEnumerable? nonErrorMessages = trxMessages?.Messages.Where(x => x is not StandardErrorTrxMessage).Select(x => x.Message); + if (nonErrorMessages?.Any() == true) + { + output.Add(new XElement("StdOut", RemoveInvalidXmlChar(string.Join(Environment.NewLine, nonErrorMessages)))); + } + + IEnumerable? errorMessages = trxMessages?.Messages.Where(x => x is StandardErrorTrxMessage).Select(x => x.Message); + if (errorMessages?.Any() == true) + { + output.Add(new XElement("StdErr", RemoveInvalidXmlChar(string.Join(Environment.NewLine, errorMessages)))); + } + + TrxExceptionProperty? trxException = testNode.Properties.SingleOrDefault(); + if (trxException?.Message is not null || trxException?.StackTrace is not null) + { + XElement errorInfoElement = new("ErrorInfo"); + + if (trxException.Message is not null) + { + errorInfoElement.Add(new XElement("Message", RemoveInvalidXmlChar(trxException.Message))); + } + + if (trxException.StackTrace is not null) + { + errorInfoElement.Add(new XElement("StackTrace", RemoveInvalidXmlChar(trxException.StackTrace))); + } + + output.Add(errorInfoElement); + } + + // add collectorDataEntries details + if (output.HasElements && outcome != "NotExecuted") + { + unitTestResult.Add(output); + } + + if (_artifactsByTestNode.TryGetValue(testNode.Uid, out List? fileArtifacts)) + { + var resultFiles = new XElement("ResultFiles"); + + foreach (SessionFileArtifact fileArtifact in fileArtifacts) + { + resultFiles.Add(new XElement( + "ResultFile", + new XAttribute("path", fileArtifact.FileInfo.FullName))); + } + + unitTestResult.Add(resultFiles); + } + + results.Add(unitTestResult); + + // TestDefinitions + var unitTest = new XElement( + "UnitTest", + new XAttribute("name", displayName), + new XAttribute("storage", testAppModule.ToLowerInvariant()), + new XAttribute("id", id)); + + TrxCategoriesProperty? trxCategories = testNode.Properties.SingleOrDefault(); + if (trxCategories?.Categories.Length > 0) + { + unitTest.Add(new XElement("TestCategory", trxCategories.Categories.Select(c => new XElement("TestCategoryItem", new XAttribute("TestCategory", c))))); + } + + unitTest.Add(new XElement("Execution", new XAttribute("id", executionId))); + + var testMethod = new XElement( + "TestMethod", + new XAttribute("codeBase", testAppModule), + new XAttribute("adapterTypeName", $"executor://{_testFrameworkAdapter.Uid}/{_testFrameworkAdapter.Version}")); + + if (_adapterSupportTrxCapability == true) + { + string? className = testNode.Properties.SingleOrDefault()?.FullyQualifiedTypeName; + if (className is not null) + { + testMethod.SetAttributeValue("className", className); + } + } + + testMethod.SetAttributeValue("name", displayName); + + unitTest.Add(testMethod); + + // Add the test method to the test definitions if it's not already there + if (!uniqueTestDefinitionTestIds.Contains(id)) + { + testDefinitions.Add(unitTest); + uniqueTestDefinitionTestIds.Add(id); + } + + // testEntry + var testEntry = new XElement( + "TestEntry", + new XAttribute("testId", id), + new XAttribute("executionId", executionId), + new XAttribute("testListId", uncategorizedTestId)); + testEntries.Add(testEntry); + } + + testRun.Add(results); + } + + private static string AddTestSettings(XElement testRun, string testRunName) + { + var testSettings = new XElement( + "TestSettings", + new XAttribute("name", "default"), + new XAttribute("id", Guid.NewGuid())); + string runDeploymentRoot = ReplaceInvalidFileNameChars(testRunName); + testSettings.Add(new XElement("Deployment", new XAttribute("runDeploymentRoot", runDeploymentRoot))); + testRun.Add(testSettings); + return runDeploymentRoot; + } + + private void AddTimes(XElement testRun) + { + var times = new XElement( + "Times", + new XAttribute("creation", _testStartTime), + new XAttribute("queuing", _testStartTime), + new XAttribute("start", _testStartTime), + new XAttribute("finish", _clock.UtcNow)); + testRun.Add(times); + } + + private static string FormatDateTimeForRunName(DateTimeOffset date) => + + // We use custom format string to make sure that runs are sorted in the same way on all intl machines. + // This is both for directory names and for Data Warehouse. + date.ToString("yyyy-MM-dd HH:mm:ss.fff", DateTimeFormatInfo.InvariantInfo); + + private static string ReplaceInvalidFileNameChars(string fileName) + { + // Replace bad chars by this. + char replacementChar = '_'; + char[] result = new char[fileName.Length]; + + // Replace each invalid char with replacement char. + for (int i = 0; i < fileName.Length; ++i) + { + result[i] = InvalidFileNameChars.Contains(fileName[i]) ? replacementChar : fileName[i]; + } + + // We trim spaces in the end because CreateFile/Dir trim those. + string replaced = new string(result).TrimEnd(); + ArgumentGuard.Ensure(replaced.Length > 0, nameof(fileName), $"File name {fileName} is empty after removing invalid characters."); + + if (IsReservedFileName(replaced)) + { + replaced = replacementChar + replaced; // Cannot add to the end because it can have extensions. + } + + return replaced; + } + + private static bool IsReservedFileName(string fileName) => + + // CreateFile: + // The following reserved device names cannot be used as the name of a file: + // CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, + // LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9. + // Also avoid these names followed by an extension, for example, NUL.tx7. + // Windows NT: CLOCK$ is also a reserved device name. + ReservedFileNamesRegex.Match(fileName).Success; + + private static Guid GuidFromString(string data) + { +#if NETCOREAPP + int byteCount = Encoding.Unicode.GetByteCount(data); + Span hash = stackalloc byte[32]; + byte[] dataBytes = ArrayPool.Shared.Rent(byteCount); + try + { + Encoding.Unicode.GetBytes(data, dataBytes); + SHA256.HashData(dataBytes.AsSpan()[..byteCount], hash); + return new Guid(hash[..16]); + } + finally + { + ArrayPool.Shared.Return(dataBytes); + } +#else + var sha256 = SHA256.Create(); + byte[] hash = sha256.ComputeHash(Encoding.Unicode.GetBytes(data)); + byte[] bytes = new byte[16]; + Array.Copy(hash, bytes, 16); + return new Guid(bytes); +#endif + } + +#if NET7_0_OR_GREATER + [GeneratedRegex(@"(?i:^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9]|CLOCK\$)(\..*)?)$", RegexOptions.None, "en-150")] + private static partial Regex BuildReservedFileNameRegex(); +#else + private static Regex BuildReservedFileNameRegex() => new(@"(?i:^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9]|CLOCK\$)(\..*)?)$"); +#endif + + // From xml spec (http://www.w3.org/TR/xml/#charsets) valid chars: + // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + // we are handling only #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] + // because C# support unicode character in range \u0000 to \uFFFF +#if NET7_0_OR_GREATER + [GeneratedRegex(@"[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD]")] + private static partial Regex BuildInvalidXmlCharReplace(); +#else + private static Regex BuildInvalidXmlCharReplace() => new(@"[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD]"); +#endif + + private static string? RemoveInvalidXmlChar(string? str) => str is null ? null : InvalidXmlCharReplace.Replace(str, InvalidXmlEvaluator); + + private static string ReplaceInvalidCharacterWithUniCodeEscapeSequence(Match match) + { + char x = match.Value[0]; + return $@"\u{(ushort)x:x4}"; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportExtensions.cs new file mode 100644 index 0000000000..69ba9fcc6a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportExtensions.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.TestReports.Resources; +using Microsoft.Testing.Extensions.TrxReport.Abstractions; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Services; +using Microsoft.Testing.Platform.TestHostControllers; + +namespace Microsoft.Testing.Extensions; + +public static class TrxReportExtensions +{ + public static void AddTrxReportProvider(this ITestApplicationBuilder builder) + { + if (builder is not TestApplicationBuilder testApplicationBuilder) + { + throw new InvalidOperationException(ExtensionResources.InvalidTestApplicationBuilderType); + } + + var commandLine = new TrxReportGeneratorCommandLine(); + + var compositeTestSessionTrxService = + new CompositeExtensionFactory(serviceProvider => + new TrxReportGenerator( + serviceProvider.GetConfiguration(), + serviceProvider.GetCommandLineOptions(), + serviceProvider.GetTestApplicationModuleInfo(), + serviceProvider.GetMessageBus(), + serviceProvider.GetSystemClock(), + serviceProvider.GetEnvironment(), + serviceProvider.GetOutputDevice(), + serviceProvider.GetTestFramework(), + serviceProvider.GetTestFrameworkCapabilities(), + commandLine, + serviceProvider.GetService(), + serviceProvider.GetLoggerFactory().CreateLogger())); + + builder.TestHost.AddTestApplicationLifecycleCallbacks(serviceProvider => + new TrxTestApplicationLifecycleCallbacks( + serviceProvider.GetCommandLineOptions(), + serviceProvider.GetEnvironment())); + builder.TestHost.AddDataConsumer(compositeTestSessionTrxService); + builder.TestHost.AddTestSessionLifetimeHandle(compositeTestSessionTrxService); + + builder.CommandLine.AddProvider(() => commandLine); + + PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + var compositeLifeTimeHandler = + new CompositeExtensionFactory(serviceProvider => + { + serviceProvider.GetLoggerFactory().CreateLogger().LogTrace($"TRX pipe name: '{pipeNameDescription.Name}"); + return new TrxProcessLifetimeHandler( + serviceProvider.GetCommandLineOptions(), + serviceProvider.GetEnvironment(), + serviceProvider.GetLoggerFactory(), + serviceProvider.GetMessageBus(), + serviceProvider.GetTestApplicationModuleInfo(), + serviceProvider.GetConfiguration(), + serviceProvider.GetSystemClock(), + serviceProvider.GetTask(), + pipeNameDescription); + }); + ((TestHostControllersManager)builder.TestHostControllers).AddDataConsumer(compositeLifeTimeHandler); + builder.TestHostControllers.AddProcessLifetimeHandler(compositeLifeTimeHandler); + builder.TestHostControllers.AddEnvironmentVariableProvider(serviceProvider => + { + serviceProvider.GetLoggerFactory().CreateLogger().LogTrace($"TRX pipe name: '{pipeNameDescription.Name}"); + return new TrxEnvironmentVariableProvider(serviceProvider.GetCommandLineOptions(), pipeNameDescription.Name); + }); + + ToolTrxCompareFactory toolTrxCompareFactory = new(); + TrxCompareToolCommandLine createTrxCompareToolCommandLine = toolTrxCompareFactory.CreateTrxCompareToolCommandLine(); + builder.CommandLine.AddProvider(() => createTrxCompareToolCommandLine); + + testApplicationBuilder.Tools.AddTool((serviceProvider) => toolTrxCompareFactory.CreateTrxCompareTool( + serviceProvider.GetCommandLineOptions(), + serviceProvider.GetOutputDevice(), + serviceProvider.GetRequiredService())); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxTestApplicationLifecycleCallbacks.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxTestApplicationLifecycleCallbacks.cs new file mode 100644 index 0000000000..1dfe5a1f0d --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxTestApplicationLifecycleCallbacks.cs @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.TestReports.Resources; +using Microsoft.Testing.Extensions.TrxReport.Abstractions.Serializers; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Models; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; + +internal class TrxTestApplicationLifecycleCallbacks : ITestApplicationLifecycleCallbacks, IDisposable +{ + private readonly bool _isEnabled; + private readonly IEnvironment _environment; + + public TrxTestApplicationLifecycleCallbacks( + ICommandLineOptions commandLineOptionsService, + IEnvironment environment) + { + _isEnabled = + + // TrxReportGenerator is enabled only when trx report is enabled + commandLineOptionsService.IsOptionSet(TrxReportGeneratorCommandLine.TrxReportOptionName) && + + // TestController is not used when we run in server mode + !commandLineOptionsService.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey) && + + // If crash dump is not enabled we run trx in-process only + commandLineOptionsService.IsOptionSet(CrashDumpCommandLineOptions.CrashDumpOptionName); + + _environment = environment; + } + + public NamedPipeClient? NamedPipeClient { get; private set; } + + public string Uid { get; } = nameof(TrxTestApplicationLifecycleCallbacks); + + /// + public string Version { get; } = AppVersion.DefaultSemVer; + + /// + public string DisplayName { get; } = ExtensionResources.TrxReportGeneratorDisplayName; + + /// + public string Description { get; } = ExtensionResources.TrxReportGeneratorDescription; + + /// + public Task IsEnabledAsync() => Task.FromResult(_isEnabled); + + public Task AfterRunAsync(int exitCode, CancellationToken cancellation) => Task.CompletedTask; + + public async Task BeforeRunAsync(CancellationToken cancellationToken) + { + if (!_isEnabled || cancellationToken.IsCancellationRequested) + { + return; + } + + try + { + if (_isEnabled) + { + string? namedPipeName = _environment.GetEnvironmentVariable(TrxEnvironmentVariableProvider.TRXNAMEDPIPENAME) + ?? throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxReportGeneratorMissingTrxNamedPipeEnvironmentVariable, TrxEnvironmentVariableProvider.TRXNAMEDPIPENAME)); + NamedPipeClient = new NamedPipeClient(namedPipeName); + NamedPipeClient.RegisterSerializer(new ReportFileNameRequestSerializer(), typeof(ReportFileNameRequest)); + NamedPipeClient.RegisterSerializer(new TestAdapterInformationRequestSerializer(), typeof(TestAdapterInformationRequest)); + NamedPipeClient.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + + // Connect to the named pipe server + await NamedPipeClient.ConnectAsync(cancellationToken).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); + } + } + catch (OperationCanceledException ex) when (ex.CancellationToken == cancellationToken) + { + // Do nothing, we're stopping + } + } + + public void Dispose() => NamedPipeClient?.Dispose(); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/build/Microsoft.Testing.Extensions.TrxReport.props b/src/Platform/Microsoft.Testing.Extensions.TrxReport/build/Microsoft.Testing.Extensions.TrxReport.props new file mode 100644 index 0000000000..4234fd10df --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/build/Microsoft.Testing.Extensions.TrxReport.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/buildMultiTargeting/Microsoft.Testing.Extensions.TrxReport.props b/src/Platform/Microsoft.Testing.Extensions.TrxReport/buildMultiTargeting/Microsoft.Testing.Extensions.TrxReport.props new file mode 100644 index 0000000000..affb8f1ab0 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/buildMultiTargeting/Microsoft.Testing.Extensions.TrxReport.props @@ -0,0 +1,14 @@ + + + + + + Microsoft.Testing.Extensions.TrxReport + Microsoft.Testing.Extensions.TrxReport.TestingPlatformBuilderHook + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/buildTransitive/Microsoft.Testing.Extensions.TrxReport.props b/src/Platform/Microsoft.Testing.Extensions.TrxReport/buildTransitive/Microsoft.Testing.Extensions.TrxReport.props new file mode 100644 index 0000000000..4234fd10df --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/buildTransitive/Microsoft.Testing.Extensions.TrxReport.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/BannedSymbols.txt new file mode 100644 index 0000000000..13daece7e1 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/BannedSymbols.txt @@ -0,0 +1,5 @@ +T:System.ArgumentNullException; Use 'ArgumentGuard' class instead +M:System.String.IsNullOrEmpty(System.String); Use 'RoslynString.IsNullOrEmpty' instead +M:System.String.IsNullOrWhiteSpace(System.String); Use 'RoslynString.IsNullOrWhiteSpace' instead +M:System.Diagnostics.Debug.Assert(System.Boolean); Use 'RoslynDebug.Assert' instead +M:System.Diagnostics.Debug.Assert(System.Boolean,System.String); Use 'RoslynDebug.Assert' instead diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Capabilities/IVSTestFlattenedTestNodesReportCapability.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Capabilities/IVSTestFlattenedTestNodesReportCapability.cs new file mode 100644 index 0000000000..c433fe18f8 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Capabilities/IVSTestFlattenedTestNodesReportCapability.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Capabilities.TestFramework; + +namespace Microsoft.Testing.Extensions.VSTestBridge.Capabilities; + +/// +/// A capability to indicate whether the VSTest adapter supports flattened test nodes report. +/// This corresponds to the way Visual Studio Test Explorer displays tests. +/// +internal interface IVSTestFlattenedTestNodesReportCapability : ITestFrameworkCapability +{ + /// + /// Gets a value indicating whether a flag indicating whether the capability is supported. + /// + bool IsSupported { get; } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Capabilities/VSTestBridgeExtensionBaseCapabilities.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Capabilities/VSTestBridgeExtensionBaseCapabilities.cs new file mode 100644 index 0000000000..bcf444bd2b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Capabilities/VSTestBridgeExtensionBaseCapabilities.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.TrxReport.Abstractions; +using Microsoft.Testing.Platform.Capabilities.TestFramework; + +namespace Microsoft.Testing.Extensions.VSTestBridge.Capabilities; + +public sealed class VSTestBridgeExtensionBaseCapabilities : ITrxReportCapability, IVSTestFlattenedTestNodesReportCapability, INamedFeatureCapability +{ + private const string MultiRequestSupport = "experimental_multiRequestSupport"; + private const string VSTestProviderSupport = "vstestProvider"; + + /// + bool IVSTestFlattenedTestNodesReportCapability.IsSupported { get; } = true; + + /// + bool ITrxReportCapability.IsSupported { get; } = true; + + /// + /// Gets a value indicating whether a flag indicating whether the trx report capability is enabled. + /// + public bool IsTrxEnabled { get; private set; } + + /// + void ITrxReportCapability.Enable() => IsTrxEnabled = true; + + bool INamedFeatureCapability.IsSupported(string featureName) => featureName is MultiRequestSupport or VSTestProviderSupport; +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/RunSettingsCommandLineOptionsProvider.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/RunSettingsCommandLineOptionsProvider.cs new file mode 100644 index 0000000000..dad26b9207 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/RunSettingsCommandLineOptionsProvider.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.VSTestBridge.Resources; +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Extensions.VSTestBridge.CommandLine; + +/// +/// A command line service provider to support VSTest .runsettings files. +/// +internal sealed class RunSettingsCommandLineOptionsProvider : ICommandLineOptionsProvider +{ + public const string RunSettingsOptionName = "settings"; + private readonly IFileSystem _fileSystem; + + public RunSettingsCommandLineOptionsProvider(IExtension extension) + : this(extension, new SystemFileSystem()) + { + } + + internal /* for testing purposes */ RunSettingsCommandLineOptionsProvider(IExtension extension, IFileSystem fileSystem) + { + Uid = extension.Uid; + DisplayName = extension.DisplayName; + Description = extension.Description; + Version = extension.Version; + _fileSystem = fileSystem; + } + + /// + public string Uid { get; } + + /// + public string Version { get; } + + /// + public string DisplayName { get; } + + /// + public string Description { get; } + + /// + public Task IsEnabledAsync() => Task.FromResult(true); + + /// + public IReadOnlyCollection GetCommandLineOptions() + => [new CommandLineOption(RunSettingsOptionName, ExtensionResources.RunSettingsOptionDescription, ArgumentArity.ExactlyOne, false)]; + + /// + public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) + { + RoslynDebug.Assert(commandOption.Name == RunSettingsOptionName); + string filePath = arguments[0]; + + if (!_fileSystem.Exists(filePath)) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, ExtensionResources.RunsettingsFileDoesNotExist, filePath)); + } + + // Even if the file exists, we want to validate we can open/read it. + if (!CanReadFile(filePath)) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, ExtensionResources.RunsettingsFileCannotBeRead, filePath)); + } + + // No problem found + return ValidationResult.ValidTask; + } + + public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) + => ValidationResult.ValidTask; + + private bool CanReadFile(string filePath) + { + try + { + using IFileStream stream = _fileSystem.NewFileStream(filePath, FileMode.Open, FileAccess.Read); + return true; + } + catch (IOException) + { + return false; + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/TestCaseFilterCommandLineOptionsProvider.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/TestCaseFilterCommandLineOptionsProvider.cs new file mode 100644 index 0000000000..edd8bfe5a1 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/TestCaseFilterCommandLineOptionsProvider.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.VSTestBridge.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; + +namespace Microsoft.Testing.Extensions.VSTestBridge.CommandLine; + +/// +/// A command line service provider bringing support for the VSTest test case. +/// +internal sealed class TestCaseFilterCommandLineOptionsProvider : ICommandLineOptionsProvider +{ + public const string TestCaseFilterOptionName = "filter"; + + public TestCaseFilterCommandLineOptionsProvider(IExtension extension) + { + Uid = extension.Uid; + DisplayName = extension.DisplayName; + Description = extension.Description; + Version = extension.Version; + } + + /// + public string Uid { get; } + + /// + public string Version { get; } + + /// + public string DisplayName { get; } + + /// + public string Description { get; } + + /// + public Task IsEnabledAsync() => Task.FromResult(true); + + /// + public IReadOnlyCollection GetCommandLineOptions() => + [ + new(TestCaseFilterOptionName, ExtensionResources.TestCaseFilterOptionDescription, ArgumentArity.ExactlyOne, false) + ]; + + /// + public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) + => ValidationResult.ValidTask; + + public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) + => ValidationResult.ValidTask; +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/TestRunParametersCommandLineOptionsProvider.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/TestRunParametersCommandLineOptionsProvider.cs new file mode 100644 index 0000000000..1646d8df09 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/TestRunParametersCommandLineOptionsProvider.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.VSTestBridge.Resources; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; + +namespace Microsoft.Testing.Extensions.VSTestBridge.CommandLine; + +internal sealed class TestRunParametersCommandLineOptionsProvider : ICommandLineOptionsProvider +{ + public const string TestRunParameterOptionName = "test-parameter"; + + public TestRunParametersCommandLineOptionsProvider(IExtension extension) + { + Uid = extension.Uid; + DisplayName = extension.DisplayName; + Description = extension.Description; + Version = extension.Version; + } + + /// + public string Uid { get; } + + /// + public string Version { get; } + + /// + public string DisplayName { get; } + + /// + public string Description { get; } + + /// + public Task IsEnabledAsync() => Task.FromResult(true); + + /// + public IReadOnlyCollection GetCommandLineOptions() + => [new CommandLineOption(TestRunParameterOptionName, ExtensionResources.TestRunParameterOptionDescription, ArgumentArity.OneOrMore, false)]; + + /// + public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) + => ValidationResult.ValidTask; + + /// + public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) + { + foreach (string argument in arguments) + { + if (!argument.Contains('=')) + { + return ValidationResult.InvalidTask(string.Format(CultureInfo.CurrentCulture, ExtensionResources.TestRunParameterOptionArgumentIsNotParameter, argument)); + } + } + + return ValidationResult.ValidTask; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Configurations/RunSettingsConfigurationProvider.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Configurations/RunSettingsConfigurationProvider.cs new file mode 100644 index 0000000000..55ec90ffa5 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Configurations/RunSettingsConfigurationProvider.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Xml.Linq; + +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Extensions.VSTestBridge.Configurations; + +internal sealed class RunSettingsConfigurationProvider : IConfigurationSource, IConfigurationProvider +{ + private readonly string _runsettings; + + public RunSettingsConfigurationProvider(string runSettings) + { + _runsettings = runSettings; + } + + /// + public string Uid { get; } = nameof(RunSettingsConfigurationProvider); + + /// + public string Version { get; } = AppVersion.DefaultSemVer; + + /// + public string DisplayName { get; } = "VSTest Helpers: runsettings configuration"; + + /// + public string Description { get; } = "Configuration source to bridge VSTest xml runsettings configuration into Microsoft Testing Platform configuration model."; + + /// + public Task IsEnabledAsync() => Task.FromResult(true); + + /// + public Task LoadAsync() => Task.CompletedTask; + + /// + public bool TryGet(string key, out string? value) + { + if (key == PlatformConfigurationConstants.PlatformResultDirectory) + { + var document = XDocument.Parse(_runsettings); + value = document.Element("RunSettings")?.Element("RunConfiguration")?.Element("ResultsDirectory")?.Value; + if (value is not null) + { + return true; + } + } + + value = null; + return false; + } + + /// + public IConfigurationProvider Build() + => new RunSettingsConfigurationProvider(_runsettings); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/Constants.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/Constants.cs new file mode 100644 index 0000000000..aa1c7c6582 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/Constants.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Extensions.VSTestBridge; + +internal static class Constants +{ + public const string ExecutorUri = "executor://testingplatform-bridge/v1"; +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/DebugUtils.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/DebugUtils.cs new file mode 100644 index 0000000000..7b26b2254f --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/DebugUtils.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Extensions.VSTestBridge.Helpers; + +internal static class DebugUtils +{ + private const string VSTestBridgeAttachDebuggerEnvVar = "TESTINGPLATFORM_VSTESTBRIDGE_ATTACH_DEBUGGER"; + + public static void LaunchAttachDebugger() + { + if (Environment.GetEnvironmentVariable(VSTestBridgeAttachDebuggerEnvVar) == "1") + { + System.Diagnostics.Debugger.Launch(); + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/SynchronousAwaiter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/SynchronousAwaiter.cs new file mode 100644 index 0000000000..8db016da73 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/SynchronousAwaiter.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Extensions.VSTestBridge.Helpers; + +/// +/// A helper class to provide synchronous awaiter. Because of vstest sync APIs we need to wait synchronously. +/// +internal static class SynchronousAwaiter +{ + public static void Await(this Task valueTask, bool busyWait = true) + { + if (busyWait) + { + var spin = default(SpinWait); + while (!valueTask.IsCompleted) + { + spin.SpinOnce(); + } + + // We want to observe the exception + valueTask.GetAwaiter().GetResult(); + } + else + { + valueTask.GetAwaiter().GetResult(); + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/TestApplicationBuilderExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/TestApplicationBuilderExtensions.cs new file mode 100644 index 0000000000..92f0d489e3 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/TestApplicationBuilderExtensions.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Extensions; + +namespace Microsoft.Testing.Extensions.VSTestBridge.Helpers; + +/// +/// A set of helper methods to register the VSTest services into Microsoft Testing Platform. +/// +public static class TestApplicationBuilderExtensions +{ + /// + /// Allows to register the VSTest filter service. + /// + /// The test application builder. + /// The extension that will be used as the source of registration for this helper service. + public static void AddTestCaseFilterService(this ITestApplicationBuilder builder, IExtension extension) + => builder.CommandLine.AddProvider(() => new TestCaseFilterCommandLineOptionsProvider(extension)); + + /// + /// Allows to register the VSTest runsettings service. + /// + /// The test application builder. + /// The extension that will be used as the source of registration for this helper service. + public static void AddRunSettingsService(this ITestApplicationBuilder builder, IExtension extension) + => builder.CommandLine.AddProvider(() => new RunSettingsCommandLineOptionsProvider(extension)); + + /// + /// Allows to register the VSTest TestRunParameters service. + /// + /// The test application builder. + /// The extension that will be used as the source of registration for this helper service. + public static void AddTestRunParametersService(this ITestApplicationBuilder builder, IExtension extension) + => builder.CommandLine.AddProvider(() => new TestRunParametersCommandLineOptionsProvider(extension)); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/VSTestTestNodeProperties.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/VSTestTestNodeProperties.cs new file mode 100644 index 0000000000..9a7d6b2daa --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/VSTestTestNodeProperties.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Extensions.VSTestBridge; + +internal static class VSTestTestNodeProperties +{ + internal const string Prefix = "vstest."; + public const string OriginalExecutorUriPropertyName = "vstest.original-executor-uri"; + + public static class TestNode + { + public const string UidPropertyName = "vstest.testnode.uid"; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Microsoft.Testing.Extensions.VSTestBridge.csproj b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Microsoft.Testing.Extensions.VSTestBridge.csproj new file mode 100644 index 0000000000..2ad5e86868 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Microsoft.Testing.Extensions.VSTestBridge.csproj @@ -0,0 +1,55 @@ + + + + netstandard2.0;$(MicrosoftTestingTargetFrameworks) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + ExtensionResources.resx + + + + + + ResXFileCodeGenerator + ExtensionResources.Designer.cs + + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/Condition.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/Condition.cs new file mode 100644 index 0000000000..1f54b44d53 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/Condition.cs @@ -0,0 +1,375 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// NOTE: This file is copied as-is from VSTest source code. +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Text; + +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; + +#pragma warning disable SA1602 // Enumeration items should be documented +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +internal enum Operation +{ + Equal, + NotEqual, + Contains, + NotContains, +} + +/// +/// Operator in order of precedence. +/// Precedence(And) > Precedence(Or) +/// Precedence of OpenBrace and CloseBrace operators is not used, instead parsing code takes care of same. +/// +internal enum Operator +{ + None, + Or, + And, + OpenBrace, + CloseBrace, +} + +/// +/// Represents a condition in filter expression. +/// +[ExcludeFromCodeCoverage] // Helper copied from VSTest source code +internal sealed class Condition +{ + /// + /// Default property name which will be used when filter has only property value. + /// + public const string DefaultPropertyName = "FullyQualifiedName"; + + /// + /// Default operation which will be used when filter has only property value. + /// + public const Operation DefaultOperation = Operation.Contains; + + internal Condition(string name, Operation operation, string value) + { + Name = name; + Operation = operation; + Value = value; + } + + /// + /// Gets toolName of the property used in condition. + /// + internal string Name { get; private set; } + + /// + /// Gets value for the property. + /// + internal string Value { get; private set; } + + /// + /// Gets operation to be performed. + /// + internal Operation Operation { get; private set; } + + /// + /// Evaluate this condition for testObject. + /// + internal bool Evaluate(Func propertyValueProvider) + { + ValidateArg.NotNull(propertyValueProvider, nameof(propertyValueProvider)); + bool result = false; + string[]? multiValue = GetPropertyValue(propertyValueProvider); + switch (Operation) + { + case Operation.Equal: + // if any value in multi-valued property matches 'this.Value', for Equal to evaluate true. + if (multiValue != null) + { + foreach (string propertyValue in multiValue) + { + result = result || string.Equals(propertyValue, Value, StringComparison.OrdinalIgnoreCase); + if (result) + { + break; + } + } + } + + break; + + case Operation.NotEqual: + // all values in multi-valued property should not match 'this.Value' for NotEqual to evaluate true. + result = true; + + // if value is null. + if (multiValue != null) + { + foreach (string propertyValue in multiValue) + { + result = result && !string.Equals(propertyValue, Value, StringComparison.OrdinalIgnoreCase); + if (!result) + { + break; + } + } + } + + break; + + case Operation.Contains: + // if any value in multi-valued property contains 'this.Value' for 'Contains' to be true. + if (multiValue != null) + { + foreach (string propertyValue in multiValue) + { + RoslynDebug.Assert(propertyValue != null, "PropertyValue can not be null."); + result = result || propertyValue.Contains(Value, StringComparison.OrdinalIgnoreCase); + if (result) + { + break; + } + } + } + + break; + + case Operation.NotContains: + // all values in multi-valued property should not contain 'this.Value' for NotContains to evaluate true. + result = true; + + if (multiValue != null) + { + foreach (string propertyValue in multiValue) + { + RoslynDebug.Assert(propertyValue != null, "PropertyValue can not be null."); + result = result && !propertyValue.Contains(Value, StringComparison.OrdinalIgnoreCase); + if (!result) + { + break; + } + } + } + + break; + } + + return result; + } + + /// + /// Returns a condition object after parsing input string of format 'Operation ]]>'. + /// + internal static Condition Parse(string? conditionString) + { + if (RoslynString.IsNullOrWhiteSpace(conditionString)) + { + ThrownFormatExceptionForInvalidCondition(conditionString); + } + + string[] parts = TokenizeFilterConditionString(conditionString).ToArray(); + if (parts.Length == 1) + { + // If only parameter values is passed, create condition with default property name, + // default operation and given condition string as parameter value. + return new Condition(DefaultPropertyName, DefaultOperation, FilterHelper.Unescape(conditionString!.Trim())); + } + + if (parts.Length != 3) + { + ThrownFormatExceptionForInvalidCondition(conditionString); + } + + for (int index = 0; index < 3; index++) + { + if (RoslynString.IsNullOrWhiteSpace(parts[index])) + { + ThrownFormatExceptionForInvalidCondition(conditionString); + } + + parts[index] = parts[index].Trim(); + } + + Operation operation = GetOperator(parts[1]); + Condition condition = new(parts[0], operation, FilterHelper.Unescape(parts[2])); + return condition; + } + + [DoesNotReturn] + private static void ThrownFormatExceptionForInvalidCondition(string? conditionString) => throw new FormatException( + string.Format(CultureInfo.CurrentCulture, "Incorrect format for TestCaseFilter {0}. Specify the correct format and try again. Note that the incorrect format can lead to no test getting executed..", + string.Format(CultureInfo.CurrentCulture, "Error: Invalid Condition '{0}'", conditionString))); + + /// + /// Check if condition validates any property in properties. + /// + internal bool ValidForProperties(IEnumerable properties, Func? propertyProvider) + { + bool valid = false; + + if (properties.Contains(Name, StringComparer.OrdinalIgnoreCase)) + { + valid = true; + + // Check if operation ~ (Contains) is on property of type string. + if (Operation == Operation.Contains) + { + valid = ValidForContainsOperation(propertyProvider); + } + } + + return valid; + } + + private bool ValidForContainsOperation(Func? propertyProvider) + { + bool valid = true; + + // It is OK for propertyProvider to be null, no syntax check will happen. + + // Check validity of operator only if related TestProperty is non-null. + // if null, it might be custom validation ignore it. + TestProperty? testProperty = propertyProvider?.Invoke(Name); + if (testProperty != null) + { + Type propertyType = testProperty.GetValueType(); + valid = typeof(string) == propertyType || + typeof(string[]) == propertyType; + } + + return valid; + } + + /// + /// Return Operation corresponding to the operationString. + /// + private static Operation GetOperator(string operationString) => operationString switch + { + "=" => Operation.Equal, + "!=" => Operation.NotEqual, + "~" => Operation.Contains, + "!~" => Operation.NotContains, + _ => throw new FormatException( + string.Format(CultureInfo.CurrentCulture, "Incorrect format for TestCaseFilter {0}. Specify the correct format and try again. Note that the incorrect format can lead to no test getting executed..", + string.Format(CultureInfo.CurrentCulture, "Error: Invalid operator '{0}'", operationString))), + }; + + /// + /// Returns property value for Property using propertyValueProvider. + /// + private string[]? GetPropertyValue(Func propertyValueProvider) + { + object? propertyValue = propertyValueProvider(Name); + if (propertyValue != null) + { + if (propertyValue is not string[] multiValue) + { + multiValue = new string[1]; + multiValue[0] = propertyValue.ToString()!; + } + + return multiValue; + } + + return null; + } + + internal static IEnumerable TokenizeFilterConditionString(string str) + { + ArgumentGuard.IsNotNull(str); + return TokenizeFilterConditionStringWorker(str); + + static IEnumerable TokenizeFilterConditionStringWorker(string s) + { + StringBuilder tokenBuilder = new(); + + char last = '\0'; + for (int i = 0; i < s.Length; ++i) + { + char current = s[i]; + + if (last == FilterHelper.EscapeCharacter) + { + // Don't check if `current` is one of the special characters here. + // Instead, we blindly let any character follows '\' pass though and + // relies on `FilterHelpers.Unescape` to report such errors. + tokenBuilder.Append(current); + + if (current == FilterHelper.EscapeCharacter) + { + // We just encountered double backslash (i.e. escaped '\'), therefore set `last` to '\0' + // so the second '\' (i.e. current) will not be treated as the prefix of escape sequence + // in next iteration. + current = '\0'; + } + } + else + { + switch (current) + { + case '=': + if (tokenBuilder.Length > 0) + { + yield return tokenBuilder.ToString(); + tokenBuilder.Clear(); + } + + yield return "="; + break; + + case '!': + if (tokenBuilder.Length > 0) + { + yield return tokenBuilder.ToString(); + tokenBuilder.Clear(); + } + + // Determine if this is a "!=" or "!~" or just a single "!". + int next = i + 1; + if (next < s.Length && s[next] == '=') + { + i = next; + current = '='; + yield return "!="; + } + else if (next < s.Length && s[next] == '~') + { + i = next; + current = '~'; + yield return "!~"; + } + else + { + yield return "!"; + } + + break; + + case '~': + if (tokenBuilder.Length > 0) + { + yield return tokenBuilder.ToString(); + tokenBuilder.Clear(); + } + + yield return "~"; + break; + + default: + tokenBuilder.Append(current); + break; + } + } + + last = current; + } + + if (tokenBuilder.Length > 0) + { + yield return tokenBuilder.ToString(); + } + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ContextAdapterBase.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ContextAdapterBase.cs new file mode 100644 index 0000000000..ba561ce981 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ContextAdapterBase.cs @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +internal abstract class ContextAdapterBase +{ + public ContextAdapterBase(ICommandLineOptions commandLineOptions) + { + if (commandLineOptions.TryGetOptionArgumentList( + TestCaseFilterCommandLineOptionsProvider.TestCaseFilterOptionName, + out string[]? filterExpressions) + && filterExpressions is not null + && filterExpressions.Length == 1) + { + FilterExpressionWrapper = new(filterExpressions[0]); + } + } + + protected FilterExpressionWrapper? FilterExpressionWrapper { get; set; } + + // NOTE: Implementation is borrowed from VSTest + // MSTest relies on this method existing and access it through reflection: https://github.com/microsoft/testfx/blob/main/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs#L115 + public ITestCaseFilterExpression? GetTestCaseFilter( + IEnumerable? supportedProperties, + Func propertyProvider) + { + if (FilterExpressionWrapper is null) + { + return null; + } + + if (!RoslynString.IsNullOrEmpty(FilterExpressionWrapper.ParseError)) + { + throw new TestPlatformFormatException(FilterExpressionWrapper.ParseError, FilterExpressionWrapper.FilterString); + } + + var adapterSpecificTestCaseFilter = new TestCaseFilterExpression(FilterExpressionWrapper); + string[]? invalidProperties = adapterSpecificTestCaseFilter.ValidForProperties(supportedProperties, propertyProvider); + + if (invalidProperties != null) + { + string validPropertiesString = supportedProperties == null + ? string.Empty + : string.Join(", ", supportedProperties.ToArray()); + string errorMessage = string.Format( + CultureInfo.CurrentCulture, + "No tests matched the filter because it contains one or more properties that are not valid ({0}). Specify filter expression containing valid properties ({1}).", + string.Join(", ", invalidProperties), + validPropertiesString); + + // For unsupported property don’t throw exception, just log the message. Later it is going to handle properly with TestCaseFilterExpression.MatchTestCase(). + EqtTrace.Info(errorMessage); + } + + return adapterSpecificTestCaseFilter; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/DiscoveryContextAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/DiscoveryContextAdapter.cs new file mode 100644 index 0000000000..57e52eb3e2 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/DiscoveryContextAdapter.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +/// +/// A Microsoft Testing Platform oriented implementation of the VSTest . +/// +internal sealed class DiscoveryContextAdapter : ContextAdapterBase, IDiscoveryContext +{ + public DiscoveryContextAdapter(ICommandLineOptions commandLineOptions, IRunSettings? runSettings = null) + : base(commandLineOptions) + { + RunSettings = runSettings; + } + + public IRunSettings? RunSettings { get; } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FastFilter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FastFilter.cs new file mode 100644 index 0000000000..7e0b07123a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FastFilter.cs @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// NOTE: This file is copied as-is from VSTest source code. +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Text.RegularExpressions; + +using Microsoft.Testing.Platform; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +[ExcludeFromCodeCoverage] // Helper copied from VSTest source code +internal sealed class FastFilter +{ + internal FastFilter(ImmutableDictionary> filterProperties, Operation filterOperation, Operator filterOperator) + { + ValidateArg.NotNullOrEmpty(filterProperties, nameof(filterProperties)); + + FilterProperties = filterProperties; + + IsFilteredOutWhenMatched = + (filterOperation != Operation.Equal || (filterOperator != Operator.Or && filterOperator != Operator.None)) + && (filterOperation == Operation.NotEqual && (filterOperator == Operator.And || filterOperator == Operator.None) + ? true + : throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "An error occurred while creating Fast filter."))); + } + + internal ImmutableDictionary> FilterProperties { get; } + + internal bool IsFilteredOutWhenMatched { get; } + + internal Regex? PropertyValueRegex { get; set; } + + internal string? PropertyValueRegexReplacement { get; set; } + + internal string[]? ValidForProperties(IEnumerable? properties) => properties is null + ? (string[]?)null + : FilterProperties.Keys.All(name => properties.Contains(name)) + ? null + : FilterProperties.Keys.Where(name => !properties.Contains(name)).ToArray(); + + internal bool Evaluate(Func propertyValueProvider) + { + ValidateArg.NotNull(propertyValueProvider, nameof(propertyValueProvider)); + + bool matched = false; + foreach (string name in FilterProperties.Keys) + { + // If there is no value corresponding to given name, treat it as unmatched. + if (!TryGetPropertyValue(name, propertyValueProvider, out string? singleValue, out string[]? multiValues)) + { + continue; + } + + if (singleValue != null) + { + string? value = PropertyValueRegex == null ? singleValue : ApplyRegex(singleValue); + matched = value != null && FilterProperties[name].Contains(value); + } + else + { + IEnumerable? values = PropertyValueRegex == null ? multiValues : multiValues?.Select(value => ApplyRegex(value)); + matched = values?.Any(result => result != null && FilterProperties[name].Contains(result)) == true; + } + + if (matched) + { + break; + } + } + + return IsFilteredOutWhenMatched ? !matched : matched; + } + + /// + /// Apply regex matching or replacement to given value. + /// + /// For matching, returns the result of matching, null if no match found. For replacement, returns the result of replacement. + private string? ApplyRegex(string value) + { + RoslynDebug.Assert(PropertyValueRegex != null); + + string? result = null; + if (PropertyValueRegexReplacement == null) + { + Match match = PropertyValueRegex!.Match(value); + if (match.Success) + { + result = match.Value; + } + } + else + { + result = PropertyValueRegex!.Replace(value, PropertyValueRegexReplacement); + } + + return result; + } + + /// + /// Returns property value for Property using propertyValueProvider. + /// + private static bool TryGetPropertyValue(string name, Func propertyValueProvider, out string? singleValue, out string[]? multiValues) + { + object? propertyValue = propertyValueProvider(name); + if (propertyValue != null) + { + multiValues = propertyValue as string[]; + singleValue = multiValues == null ? propertyValue.ToString() : null; + return true; + } + + singleValue = null; + multiValues = null; + return false; + } + + internal static Builder CreateBuilder() => new(); + + internal sealed class Builder + { + private readonly ImmutableDictionary.Builder>.Builder _filterDictionaryBuilder = ImmutableDictionary.CreateBuilder.Builder>(StringComparer.OrdinalIgnoreCase); + + private bool _operatorEncountered; + private Operator _fastFilterOperator = Operator.None; + + private bool _conditionEncountered; + private Operation _fastFilterOperation; + + private bool _containsValidFilter = true; + + internal bool ContainsValidFilter => _containsValidFilter && _conditionEncountered; + + internal void AddOperator(Operator @operator) + { + if (_containsValidFilter && (@operator == Operator.And || @operator == Operator.Or)) + { + if (_operatorEncountered) + { + _containsValidFilter = _fastFilterOperator == @operator; + } + else + { + _operatorEncountered = true; + _fastFilterOperator = @operator; + if ((_fastFilterOperation == Operation.NotEqual && _fastFilterOperator == Operator.Or) + || (_fastFilterOperation == Operation.Equal && _fastFilterOperator == Operator.And)) + { + _containsValidFilter = false; + } + } + } + else + { + _containsValidFilter = false; + } + } + + internal void AddCondition(Condition condition) + { + if (!_containsValidFilter) + { + return; + } + + if (_conditionEncountered) + { + if (condition.Operation == _fastFilterOperation) + { + AddProperty(condition.Name, condition.Value); + } + else + { + _containsValidFilter = false; + } + } + else + { + _conditionEncountered = true; + _fastFilterOperation = condition.Operation; + AddProperty(condition.Name, condition.Value); + + // Don't support `Contains`. + if (_fastFilterOperation is not Operation.Equal and not Operation.NotEqual) + { + _containsValidFilter = false; + } + } + } + + private void AddProperty(string name, string value) + { + if (!_filterDictionaryBuilder.TryGetValue(name, out ImmutableHashSet.Builder? values)) + { + values = ImmutableHashSet.CreateBuilder(StringComparer.OrdinalIgnoreCase); + _filterDictionaryBuilder.Add(name, values); + } + + values.Add(value); + } + + internal FastFilter? ToFastFilter() => ContainsValidFilter + ? new FastFilter( + _filterDictionaryBuilder.ToImmutableDictionary(kvp => kvp.Key, kvp => (ISet)_filterDictionaryBuilder[kvp.Key].ToImmutable()), + _fastFilterOperation, + _fastFilterOperator) + : null; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpression.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpression.cs new file mode 100644 index 0000000000..567f058e2a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpression.cs @@ -0,0 +1,411 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// NOTE: This file is copied as-is from VSTest source code. +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +/// +/// Represents an expression tree. +/// Supports: +/// Logical Operators: !, | +/// Equality Operators: =, != +/// Parenthesis (, ) for grouping. +/// +[ExcludeFromCodeCoverage] // Helper copied from VSTest source code +internal sealed partial class FilterExpression +{ + private const string TestCaseFilterFormatException = "Incorrect format for TestCaseFilter {0}. Specify the correct format and try again. Note that the incorrect format can lead to no test getting executed."; + + /// + /// Condition, if expression is conditional expression. + /// + private readonly Condition? _condition; + + /// + /// Left operand, when expression is logical expression. + /// + private readonly FilterExpression? _left; + + /// + /// Right operand, when expression is logical expression. + /// + private readonly FilterExpression? _right; + + /// + /// If logical expression is using logical And operator. + /// + private readonly bool _areJoinedByAnd; + + private FilterExpression(FilterExpression left, FilterExpression right, bool areJoinedByAnd) + { + ArgumentGuard.IsNotNull(left); + ArgumentGuard.IsNotNull(right); + _left = left; + _right = right; + _areJoinedByAnd = areJoinedByAnd; + } + + private FilterExpression(Condition condition) + { + ArgumentGuard.IsNotNull(condition); + _condition = condition; + } + + /// + /// Create a new filter expression 'And'ing 'this' with 'filter'. + /// + private FilterExpression And(FilterExpression filter) => new(this, filter, true); + + /// + /// Create a new filter expression 'Or'ing 'this' with 'filter'. + /// + private FilterExpression Or(FilterExpression filter) => new(this, filter, false); + + /// + /// Process the given operator from the filterStack. + /// Puts back the result of operation back to filterStack. + /// + private static void ProcessOperator(Stack filterStack, Operator op) + { + if (op == Operator.And) + { + if (filterStack.Count < 2) + { + throw new FormatException(string.Format(CultureInfo.CurrentCulture, TestCaseFilterFormatException, "Error: Missing operand")); + } + + FilterExpression filterRight = filterStack.Pop(); + FilterExpression filterLeft = filterStack.Pop(); + FilterExpression result = filterLeft.And(filterRight); + filterStack.Push(result); + } + else if (op == Operator.Or) + { + if (filterStack.Count < 2) + { + throw new FormatException(string.Format(CultureInfo.CurrentCulture, TestCaseFilterFormatException, "Error: Missing operand")); + } + + FilterExpression filterRight = filterStack.Pop(); + FilterExpression filterLeft = filterStack.Pop(); + FilterExpression result = filterLeft.Or(filterRight); + filterStack.Push(result); + } + else if (op == Operator.OpenBrace) + { + throw new FormatException(string.Format(CultureInfo.CurrentCulture, TestCaseFilterFormatException, "Error: Missing ')'")); + } + else + { + Debug.Fail("ProcessOperator called for Unexpected operator."); + throw new FormatException(string.Format(CultureInfo.CurrentCulture, TestCaseFilterFormatException, string.Empty)); + } + } + + /// + /// True, if filter is valid for given set of properties. + /// When False, invalidProperties would contain properties making filter invalid. + /// + internal string[]? ValidForProperties(IEnumerable? properties, Func? propertyProvider) + { + // if null, initialize to empty list so that invalid properties can be found. + properties ??= []; + + return IterateFilterExpression((current, result) => + { + // Only the leaves have a condition value. + if (current._condition != null) + { + bool valid = current._condition.ValidForProperties(properties, propertyProvider); + + // If it's not valid will add it to the function's return array. + return !valid ? [current._condition.Name] : null; + } + + // Concatenate the children node's result to get their parent result. + string[]? invalidRight = current._right != null ? result.Pop() : null; + string[]? invalidProperties = current._left != null ? result.Pop() : null; + + if (invalidProperties == null) + { + invalidProperties = invalidRight; + } + else if (invalidRight != null) + { + invalidProperties = invalidProperties.Concat(invalidRight).ToArray(); + } + + return invalidProperties; + }); + } + + /// + /// Return FilterExpression after parsing the given filter expression, and a FastFilter when possible. + /// + internal static FilterExpression Parse(string filterString, out FastFilter? fastFilter) + { + ValidateArg.NotNull(filterString, nameof(filterString)); + + // Below parsing doesn't error out on pattern (), so explicitly search for that (empty parenthesis). + Match invalidInput = GetEmptyParenthesisPattern().Match(filterString); + if (invalidInput.Success) + { + throw new FormatException(string.Format(CultureInfo.CurrentCulture, TestCaseFilterFormatException, "Error: Empty parenthesis ( )")); + } + + IEnumerable tokens = TokenizeFilterExpressionString(filterString); + var operatorStack = new Stack(); + var filterStack = new Stack(); + + FastFilter.Builder fastFilterBuilder = FastFilter.CreateBuilder(); + + // This is based on standard parsing of in order expression using two stacks (operand stack and operator stack) + // Precedence(And) > Precedence(Or) + foreach (string inputToken in tokens) + { + string token = inputToken.Trim(); + if (RoslynString.IsNullOrEmpty(token)) + { + // ignore empty tokens + continue; + } + + switch (token) + { + case "&": + case "|": + + Operator currentOperator = Operator.And; + if (string.Equals("|", token, StringComparison.Ordinal)) + { + currentOperator = Operator.Or; + } + + fastFilterBuilder.AddOperator(currentOperator); + + // Always put only higher priority operator on stack. + // if lesser priority -- pop up the stack and process the operator to maintain operator precedence. + // if equal priority -- pop up the stack and process the operator to maintain operator associativity. + // OpenBrace is special condition. & or | can come on top of OpenBrace for case like ((a=b)&c=d) + while (true) + { + bool isEmpty = operatorStack.Count == 0; + Operator stackTopOperator = isEmpty ? Operator.None : operatorStack.Peek(); + if (isEmpty || stackTopOperator == Operator.OpenBrace || stackTopOperator < currentOperator) + { + operatorStack.Push(currentOperator); + break; + } + + stackTopOperator = operatorStack.Pop(); + ProcessOperator(filterStack, stackTopOperator); + } + + break; + + case "(": + operatorStack.Push(Operator.OpenBrace); + break; + + case ")": + // process operators from the stack till OpenBrace is found. + // If stack is empty at any time, than matching OpenBrace is missing from the expression. + if (operatorStack.Count == 0) + { + throw new FormatException(string.Format(CultureInfo.CurrentCulture, TestCaseFilterFormatException, "Error: Missing '('")); + } + + Operator temp = operatorStack.Pop(); + while (temp != Operator.OpenBrace) + { + ProcessOperator(filterStack, temp); + if (operatorStack.Count == 0) + { + throw new FormatException(string.Format(CultureInfo.CurrentCulture, TestCaseFilterFormatException, "Error: Missing '('")); + } + + temp = operatorStack.Pop(); + } + + break; + + default: + // push the operand to the operand stack. + var condition = Condition.Parse(token); + FilterExpression filter = new(condition); + filterStack.Push(filter); + + fastFilterBuilder.AddCondition(condition); + break; + } + } + + while (operatorStack.Count != 0) + { + Operator temp = operatorStack.Pop(); + ProcessOperator(filterStack, temp); + } + + if (filterStack.Count != 1) + { + throw new FormatException(string.Format(CultureInfo.CurrentCulture, TestCaseFilterFormatException, "Missing Operator '|' or '&'")); + } + + fastFilter = fastFilterBuilder.ToFastFilter(); + + return filterStack.Pop(); + } + + private T IterateFilterExpression(Func, T> getNodeValue) + { + FilterExpression? current = this; + + // Will have the nodes. + Stack filterStack = new(); + + // Will contain the nodes results to use them in thier parent result's calculation + // and at the end will have the root result. + Stack result = new(); + + do + { + // Push root's right child and then root to stack then Set root as root's left child. + while (current != null) + { + if (current._right != null) + { + filterStack.Push(current._right); + } + + filterStack.Push(current); + current = current._left; + } + + // If the popped item has a right child and the right child is at top of stack, + // then remove the right child from stack, push the root back and set root as root's right child. + current = filterStack.Pop(); + if (filterStack.Count > 0 && current._right == filterStack.Peek()) + { + filterStack.Pop(); + filterStack.Push(current); + current = current._right; + continue; + } + + result.Push(getNodeValue(current, result)); + current = null; + } + while (filterStack.Count > 0); + + RoslynDebug.Assert(result.Count == 1, "Result stack should have one element at the end."); + return result.Peek(); + } + + /// + /// Evaluate filterExpression with given propertyValueProvider. + /// + /// The property Value Provider. + /// True if evaluation is successful. + internal bool Evaluate(Func propertyValueProvider) + { + ValidateArg.NotNull(propertyValueProvider, nameof(propertyValueProvider)); + + return IterateFilterExpression((current, result) => + { + // Only the leaves have a condition value. + if (current._condition != null) + { + return current._condition.Evaluate(propertyValueProvider); + } + else + { + // & or | operator + bool rightResult = current._right != null && result.Pop(); + bool leftResult = current._left != null && result.Pop(); + + // Concatenate the children node's result to get their parent result. + return current._areJoinedByAnd ? leftResult && rightResult : leftResult || rightResult; + } + }); + } + + internal static IEnumerable TokenizeFilterExpressionString(string str) + { + ValidateArg.NotNull(str, nameof(str)); + return TokenizeFilterExpressionStringHelper(str); + + static IEnumerable TokenizeFilterExpressionStringHelper(string s) + { + StringBuilder tokenBuilder = new(); + + char last = '\0'; + for (int i = 0; i < s.Length; ++i) + { + char current = s[i]; + + if (last == FilterHelper.EscapeCharacter) + { + // Don't check if `current` is one of the special characters here. + // Instead, we blindly let any character follows '\' pass though and + // relies on `FilterHelpers.Unescape` to report such errors. + tokenBuilder.Append(current); + + if (current == FilterHelper.EscapeCharacter) + { + // We just encountered "\\" (escaped '\'), this will set last to '\0' + // so the next char will not be treated as a suffix of escape sequence. + current = '\0'; + } + } + else + { + switch (current) + { + case '(': + case ')': + case '&': + case '|': + if (tokenBuilder.Length > 0) + { + yield return tokenBuilder.ToString(); + tokenBuilder.Clear(); + } + + yield return current.ToString(); + break; + + default: + tokenBuilder.Append(current); + break; + } + } + + last = current; + } + + if (tokenBuilder.Length > 0) + { + yield return tokenBuilder.ToString(); + } + } + } + +#if NET7_0_OR_GREATER + [GeneratedRegex(@"\(\s*\)")] + private static partial Regex GetEmptyParenthesisPattern(); +#else + private static Regex GetEmptyParenthesisPattern() + => new(@"\(\s*\)"); +#endif +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpressionWrapper.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpressionWrapper.cs new file mode 100644 index 0000000000..5196f44f71 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpressionWrapper.cs @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// NOTE: This file is copied as-is from VSTest source code. +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +using Microsoft.Testing.Platform; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +[ExcludeFromCodeCoverage] // Helper copied from VSTest source code +internal class FilterExpressionWrapper +{ + /// + /// FilterExpression corresponding to filter criteria. + /// + private readonly FilterExpression? _filterExpression; + + /// + /// Exposed for testing purpose. + /// +#pragma warning disable SA1401 // Fields should be private +#pragma warning disable SA1604 // Element documentation should have summary + internal readonly FastFilter? _fastFilter; +#pragma warning restore SA1604 // Element documentation should have summary +#pragma warning restore SA1401 // Fields should be private + + /// + /// Initializes a new instance of the class. + /// Initializes FilterExpressionWrapper with given filterString and options. + /// + public FilterExpressionWrapper(string filterString, FilterOptions? options) + { + ValidateArg.NotNullOrEmpty(filterString, nameof(filterString)); + + FilterString = filterString; + FilterOptions = options; + + try + { + // We prefer fast filter when it's available. + _filterExpression = FilterExpression.Parse(filterString, out _fastFilter); + + if (UseFastFilter) + { + _filterExpression = null; + + // Property value regex is only supported for fast filter, + // so we ignore it if no fast filter is constructed. + + // TODO: surface an error message to suer. + string? regexString = options?.FilterRegEx; + if (!RoslynString.IsNullOrEmpty(regexString)) + { + RoslynDebug.Assert(options!.FilterRegExReplacement == null || options.FilterRegEx != null); + _fastFilter.PropertyValueRegex = new Regex(regexString, RegexOptions.Compiled); + _fastFilter.PropertyValueRegexReplacement = options.FilterRegExReplacement; + } + } + } + catch (FormatException ex) + { + ParseError = ex.Message; + } + catch (ArgumentException ex) + { + _fastFilter = null; + ParseError = ex.Message; + } + } + + /// + /// Initializes a new instance of the class. + /// Initializes FilterExpressionWrapper with given filterString. + /// + public FilterExpressionWrapper(string filterString) + : this(filterString, null) + { + } + + [MemberNotNullWhen(true, nameof(_fastFilter))] + private bool UseFastFilter => _fastFilter != null; + + /// + /// Gets user specified filter criteria. + /// + public string FilterString { get; } + + /// + /// Gets user specified additional filter options. + /// + public FilterOptions? FilterOptions { get; } + + /// + /// Gets parsing error (if any), when parsing 'FilterString' with built-in parser. + /// + public string? ParseError { get; } + + /// + /// Validate if underlying filter expression is valid for given set of supported properties. + /// + public string[]? ValidForProperties(IEnumerable? supportedProperties, Func? propertyProvider) + => UseFastFilter + ? _fastFilter.ValidForProperties(supportedProperties) + : _filterExpression?.ValidForProperties(supportedProperties, propertyProvider); + + /// + /// Evaluate filterExpression with given propertyValueProvider. + /// + public bool Evaluate(Func propertyValueProvider) + { + ValidateArg.NotNull(propertyValueProvider, nameof(propertyValueProvider)); + + return UseFastFilter + ? _fastFilter.Evaluate(propertyValueProvider) + : _filterExpression != null && _filterExpression.Evaluate(propertyValueProvider); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FrameworkHandlerAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FrameworkHandlerAdapter.cs new file mode 100644 index 0000000000..ceeb3cf84e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FrameworkHandlerAdapter.cs @@ -0,0 +1,175 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.VSTestBridge.Helpers; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Services; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + +using TestSessionContext = Microsoft.Testing.Platform.TestHost.TestSessionContext; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +/// +/// Bridge implementation of that forwards calls to VSTest and Microsoft Testing Platforms. +/// +internal sealed class FrameworkHandlerAdapter : IFrameworkHandle +{ + /// + /// Not null when used in the context of VSTest. + /// + private readonly IFrameworkHandle? _frameworkHandle; + private readonly ILogger _logger; + private readonly IMessageBus _messageBus; + private readonly VSTestBridgedTestFrameworkBase _adapterExtensionBase; + private readonly TestSessionContext _session; + private readonly CancellationToken _cancellationToken; + private readonly bool _isTrxEnabled; + private readonly MessageLoggerAdapter _comboMessageLogger; + private readonly string _testAssemblyPath; + + public FrameworkHandlerAdapter(VSTestBridgedTestFrameworkBase adapterExtensionBase, TestSessionContext session, string[] testAssemblyPaths, + ITestApplicationModuleInfo testApplicationModuleInfo, ILoggerFactory loggerFactory, IMessageBus messageBus, IOutputDevice outputDevice, + bool isTrxEnabled, CancellationToken cancellationToken, IFrameworkHandle? frameworkHandle = null) + { + if (testAssemblyPaths.Length == 0) + { + throw new ArgumentException($"{nameof(testAssemblyPaths)} should contain at least one test assembly."); + } + else if (testAssemblyPaths.Length > 1) + { + _testAssemblyPath = testApplicationModuleInfo.GetCurrentTestApplicationFullPath(); + + if (!testAssemblyPaths.Contains(_testAssemblyPath)) + { + throw new ArgumentException("None of the test assemblies are the test application."); + } + } + else + { + _testAssemblyPath = testAssemblyPaths[0]; + } + + _frameworkHandle = frameworkHandle; + _logger = loggerFactory.CreateLogger(); + _messageBus = messageBus; + _adapterExtensionBase = adapterExtensionBase; + _session = session; + _cancellationToken = cancellationToken; + _isTrxEnabled = isTrxEnabled; + _comboMessageLogger = new MessageLoggerAdapter(loggerFactory, outputDevice, adapterExtensionBase, frameworkHandle); + } + + /// + public bool EnableShutdownAfterTestRun + { + get => _frameworkHandle?.EnableShutdownAfterTestRun ?? false; + set + { + _logger.LogTraceAsync($"{nameof(FrameworkHandlerAdapter)}.EnableShutdownAfterTestRun: set to {value}").Await(); + if (_frameworkHandle is not null) + { + _frameworkHandle.EnableShutdownAfterTestRun = value; + } + } + } + + /// + public int LaunchProcessWithDebuggerAttached(string filePath, string? workingDirectory, string? arguments, + IDictionary? environmentVariables) + { + _logger.LogTraceAsync($"{nameof(FrameworkHandlerAdapter)}.LaunchProcessWithDebuggerAttached").Await(); + return _frameworkHandle?.LaunchProcessWithDebuggerAttached(filePath, workingDirectory, arguments, environmentVariables) + ?? -1; + } + + /// + public void RecordAttachments(IList attachmentSets) + { + _logger.LogTraceAsync($"{nameof(FrameworkHandlerAdapter)}.RecordAttachments").Await(); + _frameworkHandle?.RecordAttachments(attachmentSets); + PublishAttachmentsAsync(attachmentSets).Await(); + } + + /// + public void RecordEnd(TestCase testCase, TestOutcome outcome) + { + _logger.LogTraceAsync($"{nameof(FrameworkHandlerAdapter)}.RecordEnd").Await(); + + _cancellationToken.ThrowIfCancellationRequested(); + + testCase.FixUpTestCase(_testAssemblyPath); + + // Forward call to VSTest + _frameworkHandle?.RecordEnd(testCase, outcome); + } + + /// + public void RecordResult(TestResult testResult) + { + _logger.LogTraceAsync($"{nameof(FrameworkHandlerAdapter)}.RecordResult").Await(); + + _cancellationToken.ThrowIfCancellationRequested(); + + testResult.TestCase.FixUpTestCase(_testAssemblyPath); + + // Forward call to VSTest + _frameworkHandle?.RecordResult(testResult); + + // Publish node state change to Microsoft Testing Platform + var testNode = testResult.ToTestNode(_isTrxEnabled, _session.Client); + + var testNodeChange = new TestNodeUpdateMessage(_session.SessionUid, testNode); + _messageBus.PublishAsync(_adapterExtensionBase, testNodeChange).Await(); + + PublishAttachmentsAsync(testResult.Attachments, testNode).Await(); + } + + /// + public void RecordStart(TestCase testCase) + { + _logger.LogTraceAsync($"{nameof(FrameworkHandlerAdapter)}.RecordStart").Await(); + + _cancellationToken.ThrowIfCancellationRequested(); + + testCase.FixUpTestCase(_testAssemblyPath); + + // Forward call to VSTest + _frameworkHandle?.RecordStart(testCase); + + // Publish node state change to Microsoft Testing Platform + var testNode = testCase.ToTestNode(_isTrxEnabled, _session.Client); + testNode.Properties.Add(InProgressTestNodeStateProperty.CachedInstance); + var testNodeChange = new TestNodeUpdateMessage(_session.SessionUid, testNode); + + _messageBus.PublishAsync(_adapterExtensionBase, testNodeChange).Await(); + } + + /// + public void SendMessage(TestMessageLevel testMessageLevel, string message) + => _comboMessageLogger.SendMessage(testMessageLevel, message); + + private async Task PublishAttachmentsAsync(IEnumerable attachments, TestNode? testNode = null) + { + foreach (AttachmentSet attachmentSet in attachments) + { + foreach (UriDataAttachment attachment in attachmentSet.Attachments) + { + if (!attachment.Uri.IsFile) + { + throw new FormatException($"Test adapter {_adapterExtensionBase.DisplayName} only supports file attachments."); + } + + SessionFileArtifact fileArtifact = testNode is null + ? new SessionFileArtifact(_session.SessionUid, new(attachment.Uri.LocalPath), attachmentSet.DisplayName, attachment.Description) + : new TestNodeFileArtifact(_session.SessionUid, testNode, new(attachment.Uri.LocalPath), attachmentSet.DisplayName, attachment.Description); + await _messageBus.PublishAsync(_adapterExtensionBase, fileArtifact); + } + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/MessageLoggerAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/MessageLoggerAdapter.cs new file mode 100644 index 0000000000..44177c1620 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/MessageLoggerAdapter.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.VSTestBridge.Helpers; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +/// +/// Bridge implementation of that forwards calls to VSTest and Microsoft Testing Platforms. +/// +internal sealed class MessageLoggerAdapter : IMessageLogger, IOutputDeviceDataProducer +{ + /// + /// Not null when used in the context of VSTest. + /// + private readonly IMessageLogger? _messageLogger; + private readonly ILogger _logger; + private readonly IOutputDevice _outputDevice; + private readonly IExtension _extension; + + public MessageLoggerAdapter(ILoggerFactory loggerFactory, IOutputDevice outputDevice, IExtension extension, + IMessageLogger? messageLogger = null) + { + _outputDevice = outputDevice; + _extension = extension; + _messageLogger = messageLogger; + _logger = loggerFactory.CreateLogger(); + } + + string IExtension.Uid => _extension.Uid; + + string IExtension.Version => _extension.Version; + + string IExtension.DisplayName => _extension.DisplayName; + + string IExtension.Description => _extension.Description; + + /// + public void SendMessage(TestMessageLevel testMessageLevel, string message) + { + _messageLogger?.SendMessage(testMessageLevel, message); + + switch (testMessageLevel) + { + case TestMessageLevel.Informational: + _logger.LogInformationAsync(message).Await(); + break; + case TestMessageLevel.Warning: + _logger.LogWarningAsync(message).Await(); + _outputDevice.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateYellowConsoleColorText(message)).Await(); + break; + case TestMessageLevel.Error: + _logger.LogErrorAsync(message).Await(); + _outputDevice.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(message)).Await(); + break; + default: + throw new NotSupportedException($"Unsupported logging level '{testMessageLevel}'."); + } + } + + Task IExtension.IsEnabledAsync() => _extension.IsEnabledAsync(); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs new file mode 100644 index 0000000000..83ab28fdba --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs @@ -0,0 +1,234 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +using Microsoft.Testing.Extensions.TrxReport.Abstractions; +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.TestHost; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +/// +/// A set of extension methods to convert between the Microsoft Testing Platform and VSTest object models. +/// +internal static class ObjectModelConverters +{ + public static readonly TestProperty TestNodeUidProperty = TestProperty.Register( + VSTestTestNodeProperties.TestNode.UidPropertyName, + VSTestTestNodeProperties.TestNode.UidPropertyName, typeof(string), typeof(TestNode)); + + private static readonly TestProperty OriginalExecutorUriProperty = TestProperty.Register( + VSTestTestNodeProperties.OriginalExecutorUriPropertyName, VSTestTestNodeProperties.OriginalExecutorUriPropertyName, + typeof(Uri), typeof(TestNode)); + + /// + /// Converts a VSTest to a Microsoft Testing Platform . + /// + public static TestNode ToTestNode(this TestCase testCase, bool isTrxEnabled, ClientInfo client) + { + string testNodeUid = testCase.GetPropertyValue(TestNodeUidProperty, null) + ?? testCase.FullyQualifiedName; + + TestNode testNode = new() + { + Uid = new TestNodeUid(testNodeUid), + DisplayName = testCase.DisplayName ?? testCase.FullyQualifiedName, + }; + + CopyVSTestProperties(testCase.Properties, testNode, testCase, testCase.GetPropertyValue, isTrxEnabled, client); + if (testCase.CodeFilePath is not null) + { + testNode.Properties.Add(new TestFileLocationProperty(testCase.CodeFilePath, new(new(testCase.LineNumber, -1), new(testCase.LineNumber, -1)))); + } + + return testNode; + } + + private static void CopyVSTestProperties(IEnumerable testProperties, TestNode testNode, TestCase testCase, Func getPropertyValue, + bool isTrxEnabled, ClientInfo client) + { + List>? testNodeTraits = null; + foreach (TestProperty property in testProperties) + { + testNode.Properties.Add(new VSTestProperty(property, testCase)); + + if (isTrxEnabled) + { + // TPv2 is doing some special handling for MSTest... we should probably do the same. + // See https://github.com/microsoft/vstest/blob/main/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs#L66-L70 + if (property.Id == "MSTestDiscoverer.TestCategory" + && getPropertyValue(property) is string[] mstestCategories) + { + testNode.Properties.Add(new TrxCategoriesProperty(mstestCategories)); + } + } + + // Implement handling of specific vstest properties for VS/VS Code Test Explorer, + // see https://github.com/microsoft/testanywhere/blob/main/docs/design/proposed/IDE_Protocol_IDE_Integration.md#vstest-test-node + if (client.Id == WellKnownClients.VisualStudio) + { + if (property.Id == TestCaseProperties.Id.Id + && getPropertyValue(property) is Guid testCaseId) + { + testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.Id", testCaseId.ToString())); + } + else if (property.Id == TestCaseProperties.FullyQualifiedName.Id + && getPropertyValue(property) is string testCaseFqn) + { + testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.FullyQualifiedName", testCaseFqn)); + } + else if (property.Id == OriginalExecutorUriProperty.Id + && getPropertyValue(property) is Uri originalExecutorUri) + { + testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.original-executor-uri", originalExecutorUri.AbsoluteUri)); + } + + // The TP object holding the hierarchy property is defined on adapter utilities and we don't want to enforce that dependency + // so instead I use the string ID copied from TP. + else if (property.Id == "TestCase.Hierarchy" + && getPropertyValue(property) is string[] testCaseHierarchy + && testCaseHierarchy.Length == 4) + { + testNode.Properties.Add(new SerializableNamedArrayStringProperty("vstest.TestCase.Hierarchy", testCaseHierarchy)); + } + + // ID is defined on TraitCollection but is internal so again we copy the string here. + else if (property.Id == "TestObject.Traits" + && getPropertyValue(property) is KeyValuePair[] traits && traits.Length > 0) + { +#pragma warning disable SA1010 // Opening square brackets should be spaced correctly + testNodeTraits ??= []; +#pragma warning restore SA1010 // Opening square brackets should be spaced correctly + testNodeTraits.AddRange(traits); + } + + // TPv2 is doing some special handling for MSTest... we should probably do the same. + // See https://github.com/microsoft/vstest/blob/main/src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Converter.cs#L66-L70 + else if (property.Id == "MSTestDiscoverer.TestCategory" + && getPropertyValue(property) is string[] mstestCategories && mstestCategories.Length > 0) + { +#pragma warning disable SA1010 // Opening square brackets should be spaced correctly + testNodeTraits ??= []; +#pragma warning restore SA1010 // Opening square brackets should be spaced correctly + foreach (string category in mstestCategories) + { + testNodeTraits.Add(new(category, string.Empty)); + } + } + } + } + + // Add all the traits and categories we collected from different properties. + if (testNodeTraits != null) + { + testNode.Properties.Add(new SerializableNamedKeyValuePairsStringProperty("traits", testNodeTraits.ToArray())); + } + } + + /// + /// Converts a VSTest to a Microsoft Testing Platform . + /// + public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, ClientInfo client) + { + var testNode = testResult.TestCase.ToTestNode(isTrxEnabled, client); + CopyVSTestProperties(testResult.Properties, testNode, testResult.TestCase, testResult.GetPropertyValue, isTrxEnabled, client); + testNode.AddOutcome(testResult); + + if (isTrxEnabled) + { + testNode.Properties.Add(new TrxExceptionProperty(testResult.ErrorMessage, testResult.ErrorStackTrace)); + + if (TryParseFullyQualifiedType(testResult.TestCase.FullyQualifiedName, out string? fullyQualifiedType)) + { + testNode.Properties.Add(new TrxFullyQualifiedTypeNameProperty(fullyQualifiedType)); + } + else + { + throw new InvalidOperationException("Unable to parse fully qualified type name from test case: " + testResult.TestCase.FullyQualifiedName); + } + + testNode.Properties.Add(new TrxMessagesProperty(testResult.Messages + .Select(msg => + msg.Category switch + { + string x when x == TestResultMessage.StandardErrorCategory => new StandardErrorTrxMessage(msg.Text), + string x when x == TestResultMessage.StandardOutCategory => new StandardOutputTrxMessage(msg.Text), + string x when x == TestResultMessage.DebugTraceCategory => new DebugOrTraceTrxMessage(msg.Text), + _ => new TrxMessage(msg.Text), + }) + .ToArray())); + } + + testNode.Properties.Add(new TimingProperty(new(testResult.StartTime, testResult.EndTime, testResult.Duration), [])); + + return testNode; + } + + private static void AddOutcome(this TestNode testNode, TestResult testResult) + { + switch (testResult.Outcome) + { + case TestOutcome.Passed: + testNode.Properties.Add(PassedTestNodeStateProperty.CachedInstance); + break; + + case TestOutcome.NotFound: + testNode.Properties.Add(new ErrorTestNodeStateProperty(new VSTestException(testResult.ErrorMessage ?? "Not found", testResult.ErrorStackTrace))); + break; + + case TestOutcome.Failed: + testNode.Properties.Add(new FailedTestNodeStateProperty(new VSTestException(testResult.ErrorMessage, testResult.ErrorStackTrace))); + break; + + // It seems that NUnit inconclusive tests are reported as None which should be considered as Skipped. + case TestOutcome.None: + case TestOutcome.Skipped: + testNode.Properties.Add(SkippedTestNodeStateProperty.CachedInstance); + break; + + default: + throw new NotSupportedException($"Unsupported test outcome value '{testResult.Outcome}'"); + } + } + + internal static void FixUpTestCase(this TestCase testCase, string? testAssemblyPath = null) + { + // To help framework authors using code generator, we replace the Source property of the test case with the + // test assembly path. + if (RoslynString.IsNullOrEmpty(testCase.Source) && !RoslynString.IsNullOrEmpty(testAssemblyPath)) + { + testCase.Source = testAssemblyPath; + } + + // Because this project is the actually registered test adapter, we need to replace test framework executor + // URI by ours. + if (!testCase.Properties.Any(x => x.Id == OriginalExecutorUriProperty.Id)) + { + testCase.SetPropertyValue(OriginalExecutorUriProperty, testCase.ExecutorUri); + } + + testCase.ExecutorUri = new(Constants.ExecutorUri); + } + + private static bool TryParseFullyQualifiedType(string fullyQualifiedName, [NotNullWhen(true)] out string? fullyQualifiedType) + { + fullyQualifiedType = null; + + // Some test frameworks display arguments in the fully qualified type name, so we need to exclude them + // before looking at the last dot. + int openBracketIndex = fullyQualifiedName.IndexOf('('); + int lastDotIndexBeforeOpenBracket = openBracketIndex <= 0 + ? fullyQualifiedName.LastIndexOf('.') + : fullyQualifiedName.LastIndexOf('.', openBracketIndex - 1); + if (lastDotIndexBeforeOpenBracket <= 0) + { + return false; + } + + fullyQualifiedType = fullyQualifiedName[..lastDotIndexBeforeOpenBracket]; + return true; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunContextAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunContextAdapter.cs new file mode 100644 index 0000000000..ba279bfa59 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunContextAdapter.cs @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics; +using System.Text; +using System.Xml.Linq; + +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +/// +/// A Microsoft Testing Platform oriented implementation of the VSTest . +/// +internal sealed class RunContextAdapter : ContextAdapterBase, IRunContext +{ + public RunContextAdapter(ICommandLineOptions commandLineOptions, IRunSettings runSettings) + : base(commandLineOptions) + { + RoslynDebug.Assert(runSettings.SettingsXml is not null); + + RunSettings = runSettings; + + // Parse and take the results directory from the runsettings. + TestRunDirectory = XElement.Parse(runSettings.SettingsXml).Descendants("ResultsDirectory").SingleOrDefault()?.Value; + } + + public RunContextAdapter(ICommandLineOptions commandLineOptions, IRunSettings runSettings, TestNodeUid[] testNodeUids) + : this(commandLineOptions, runSettings) + { + // We assume that the UIDs we receive are TestCase.FullyQualifiedName values. + FilterExpressionWrapper = new(string.Join("|", testNodeUids.Select(ConvertToFullyQualifiedNameFilterString))); + } + + // NOTE: Always false as it's TPv2 oriented and so not applicable to TA. + + /// + public bool KeepAlive { get; } + + // NOTE: Always false as it's TPv2 oriented and so not applicable to TA. + + /// + public bool InIsolation { get; } + + // NOTE: Always false as it's TPv2 oriented and so not applicable to TA. + + /// + public bool IsDataCollectionEnabled { get; } + + /// + public bool IsBeingDebugged => Debugger.IsAttached; + + /// + public string? TestRunDirectory { get; } + + /// + public string? SolutionDirectory { get; } + + /// + public IRunSettings? RunSettings { get; } + + private static string ConvertToFullyQualifiedNameFilterString(TestNodeUid testNodeUid) + { + StringBuilder filterString = new("FullyQualifiedName="); + + for (int i = 0; i < testNodeUid.Value.Length; i++) + { + char currentChar = testNodeUid.Value[i]; + switch (currentChar) + { + case '\\': + case '(': + case ')': + case '&': + case '|': + case '=': + case '!': + case '~': + // If the symbol is not escaped, add an escape character. + if (i - 1 < 0 || testNodeUid.Value[i - 1] != '\\') + { + filterString.Append('\\'); + } + + filterString.Append(currentChar); + break; + + default: + filterString.Append(currentChar); + break; + } + } + + return filterString.ToString(); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsAdapter.cs new file mode 100644 index 0000000000..786f228e15 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsAdapter.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.TestHost; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +/// +/// A Microsoft Testing Platform oriented implementation of the VSTest . +/// +internal sealed class RunSettingsAdapter : IRunSettings +{ + public RunSettingsAdapter( + ICommandLineOptions commandLineOptions, + IFileSystem fileSystem, + IConfiguration configuration, + ClientInfo client, + ILoggerFactory loggerFactory) + { + string? runSettingsXml = + commandLineOptions.TryGetOptionArgumentList(RunSettingsCommandLineOptionsProvider.RunSettingsOptionName, out string[]? fileNames) + && fileNames is not null + && fileNames.Length == 1 + && fileSystem.Exists(fileNames[0]) + ? fileSystem.ReadAllText(fileNames[0]) + : Environment.GetEnvironmentVariable("TESTINGPLATFORM_EXPERIMENTAL_VSTEST_RUNSETTINGS"); + + runSettingsXml = RunSettingsPatcher.Patch(runSettingsXml, configuration, client, commandLineOptions).ToString(); + + loggerFactory.CreateLogger().LogDebug($"Execution will use the following runsettings:{Environment.NewLine}{runSettingsXml}"); + + SettingsXml = runSettingsXml; + } + + /// + public string? SettingsXml { get; } + + /// + // TODO: Needs to be implemented if used by adapters. It is not used by MSTest. + public ISettingsProvider? GetSettings(string? settingsName) => throw new NotImplementedException(); +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsPatcher.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsPatcher.cs new file mode 100644 index 0000000000..67e732bbe1 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsPatcher.cs @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Xml.Linq; + +using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.TestHost; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +internal static class RunSettingsPatcher +{ + private static readonly char[] TestRunParameterSeparator = ['=']; + + public static XDocument Patch(string? runSettingsXml, IConfiguration configuration, ClientInfo client, ICommandLineOptions commandLineOptions) + { + XDocument runSettingsDocument = PatchSettingsWithDefaults(runSettingsXml, isDesignMode: client.Id == WellKnownClients.VisualStudio, configuration); + PatchTestRunParameters(runSettingsDocument, commandLineOptions); + + return runSettingsDocument; + } + + private static XDocument PatchSettingsWithDefaults(string? runSettingsXml, bool isDesignMode, IConfiguration configuration) + { + if (RoslynString.IsNullOrWhiteSpace(runSettingsXml)) + { + runSettingsXml = """ + + + """; + } + + var document = XDocument.Parse(runSettingsXml); + if (document.Element("RunSettings") is not { } runSettingsElement) + { + throw new InvalidOperationException("Invalid runsettings, section is missing."); + } + + bool isPatchingCommentAdded = false; + if (runSettingsElement.Element("RunConfiguration") is not { } runConfigurationElement) + { + runConfigurationElement = new XElement("RunConfiguration"); + AddPatchingCommentIfNeeded(runConfigurationElement, ref isPatchingCommentAdded); + runSettingsElement.AddFirst(runConfigurationElement); + } + + if (runConfigurationElement.Element("DisableAppDomain") is null) + { + AddPatchingCommentIfNeeded(runConfigurationElement, ref isPatchingCommentAdded); + runConfigurationElement.Add(new XElement("DisableAppDomain", false)); + } + + if (runConfigurationElement.Element("DesignMode") is null) + { + AddPatchingCommentIfNeeded(runConfigurationElement, ref isPatchingCommentAdded); + runConfigurationElement.Add(new XElement("DesignMode", isDesignMode)); + } + + if (runConfigurationElement.Element("CollectSourceInformation") is null) + { + AddPatchingCommentIfNeeded(runConfigurationElement, ref isPatchingCommentAdded); + runConfigurationElement.Add(new XElement("CollectSourceInformation", false)); + } + + if (runConfigurationElement.Element("ResultsDirectory") is null) + { + AddPatchingCommentIfNeeded(runConfigurationElement, ref isPatchingCommentAdded); + runConfigurationElement.Add(new XElement("ResultsDirectory", configuration.GetTestResultDirectory())); + } + + if (isPatchingCommentAdded) + { + runConfigurationElement.Add(new XComment("End")); + } + + return document; + } + + private static void AddPatchingCommentIfNeeded(XElement element, ref bool isPatchingCommentAdded) + { + if (isPatchingCommentAdded) + { + return; + } + + element.Add(new XComment("Default configuration added by Microsoft Testing Platform")); + isPatchingCommentAdded = true; + } + + private static void PatchTestRunParameters(XDocument runSettingsDocument, ICommandLineOptions commandLineOptions) + { + if (!commandLineOptions.TryGetOptionArgumentList(TestRunParametersCommandLineOptionsProvider.TestRunParameterOptionName, out string[]? testRunParameters)) + { + return; + } + + XElement runSettingsElement = runSettingsDocument.Element("RunSettings")!; + XElement? testRunParametersElement = runSettingsElement.Element("TestRunParameters"); + if (testRunParametersElement is null) + { + testRunParametersElement = new XElement("TestRunParameters"); + runSettingsElement.Add(testRunParametersElement); + } + + XElement[] testRunParametersNodes = testRunParametersElement.Nodes().OfType().ToArray(); + foreach (string testRunParameter in testRunParameters) + { + string[] parts = testRunParameter.Split(TestRunParameterSeparator, 2); + string name = parts[0]; + string value = parts[1]; + XElement? existingElement = testRunParametersNodes.FirstOrDefault(x => x.Attribute("name")?.Value == name); + if (existingElement is not null) + { + existingElement.Attribute("value")!.Value = value; + } + else + { + var parameterElement = new XElement("Parameter"); + parameterElement.Add(new XAttribute("name", name)); + parameterElement.Add(new XAttribute("value", value)); + testRunParametersElement.Add(parameterElement); + } + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/TestCaseDiscoverySinkAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/TestCaseDiscoverySinkAdapter.cs new file mode 100644 index 0000000000..a3fd9dcfea --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/TestCaseDiscoverySinkAdapter.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.VSTestBridge.Helpers; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.Services; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + +using TestSessionContext = Microsoft.Testing.Platform.TestHost.TestSessionContext; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +/// +/// Bridge implementation of that forwards calls to VSTest and Microsoft Testing Platforms. +/// +internal sealed class TestCaseDiscoverySinkAdapter : ITestCaseDiscoverySink +{ + /// + /// Not null when used in the context of VSTest. + /// + private readonly ITestCaseDiscoverySink? _testCaseDiscoverySink; + private readonly ILogger _logger; + private readonly IMessageBus _messageBus; + private readonly bool _isTrxEnabled; + private readonly VSTestBridgedTestFrameworkBase _adapterExtension; + private readonly TestSessionContext _session; + private readonly CancellationToken _cancellationToken; + private readonly string? _testAssemblyPath; + + public TestCaseDiscoverySinkAdapter(VSTestBridgedTestFrameworkBase adapterExtension, TestSessionContext session, string[] testAssemblyPaths, + ITestApplicationModuleInfo testApplicationModuleInfo, ILoggerFactory loggerFactory, IMessageBus messageBus, bool isTrxEnabled, CancellationToken cancellationToken, ITestCaseDiscoverySink? testCaseDiscoverySink = null) + { + if (testAssemblyPaths.Length == 0) + { + throw new ArgumentException($"{nameof(testAssemblyPaths)} should contain at least one test assembly."); + } + else if (testAssemblyPaths.Length > 1) + { + _testAssemblyPath = testApplicationModuleInfo.GetCurrentTestApplicationFullPath(); + + if (!testAssemblyPaths.Contains(_testAssemblyPath)) + { + throw new ArgumentException("None of the test assemblies are the test application."); + } + } + else + { + _testAssemblyPath = testAssemblyPaths[0]; + } + + _testCaseDiscoverySink = testCaseDiscoverySink; + _logger = loggerFactory.CreateLogger(); + _messageBus = messageBus; + _isTrxEnabled = isTrxEnabled; + _adapterExtension = adapterExtension; + _session = session; + _cancellationToken = cancellationToken; + } + + /// + public void SendTestCase(TestCase discoveredTest) + { + _logger.LogTraceAsync("BridgeTestCaseDiscoverySink.SendTestCase").Await(); + + _cancellationToken.ThrowIfCancellationRequested(); + + discoveredTest.FixUpTestCase(_testAssemblyPath); + + // Forward call to VSTest + _testCaseDiscoverySink?.SendTestCase(discoveredTest); + + // Publish node state change to Microsoft Testing Platform + var testNode = discoveredTest.ToTestNode(_isTrxEnabled, _session.Client); + testNode.Properties.Add(DiscoveredTestNodeStateProperty.CachedInstance); + var testNodeChange = new TestNodeUpdateMessage(_session.SessionUid, testNode); + + _messageBus.PublishAsync(_adapterExtension, testNodeChange).Await(); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/TestCaseFilterExpression.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/TestCaseFilterExpression.cs new file mode 100644 index 0000000000..deafa248f9 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/TestCaseFilterExpression.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// NOTE: This file is copied as-is from VSTest source code. +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +/// +/// Implements ITestCaseFilterExpression, providing test case filtering functionality. +/// +internal sealed class TestCaseFilterExpression : ITestCaseFilterExpression +{ + private readonly FilterExpressionWrapper _filterWrapper; + + /// + /// If filter Expression is valid for performing TestCase matching + /// (has only supported properties, syntax etc). + /// + private readonly bool _validForMatch; + + /// + /// Initializes a new instance of the class. + /// Adapter specific filter expression. + /// + public TestCaseFilterExpression(FilterExpressionWrapper filterWrapper) + { + ArgumentGuard.IsNotNull(filterWrapper); + _filterWrapper = filterWrapper; + _validForMatch = RoslynString.IsNullOrEmpty(filterWrapper.ParseError); + } + + /// + /// Gets user specified filter criteria. + /// + public string TestCaseFilterValue => _filterWrapper.FilterString; + + /// + /// Validate if underlying filter expression is valid for given set of supported properties. + /// + public string[]? ValidForProperties(IEnumerable? supportedProperties, Func propertyProvider) + { + string[]? invalidProperties = null; + if (_filterWrapper != null && _validForMatch) + { + invalidProperties = _filterWrapper.ValidForProperties(supportedProperties, propertyProvider); + } + + return invalidProperties; + } + + /// + /// Match test case with filter criteria. + /// + public bool MatchTestCase(TestCase testCase, Func propertyValueProvider) + { + ValidateArg.NotNull(testCase, nameof(testCase)); + ValidateArg.NotNull(propertyValueProvider, nameof(propertyValueProvider)); + + if (!_validForMatch) + { + return false; + } + + if (_filterWrapper == null) + { + // can be null when parsing error occurs. Invalid filter results in no match. + return false; + } + + return _filterWrapper.Evaluate(propertyValueProvider); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/VSTestException.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/VSTestException.cs new file mode 100644 index 0000000000..84e98265c0 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/VSTestException.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +internal sealed class VSTestException : Exception +{ + public VSTestException(string? message, string? stackTrace) + : base(message) + { + StackTrace = stackTrace; + } + + public override string? StackTrace { get; } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/VSTestProperty.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/VSTestProperty.cs new file mode 100644 index 0000000000..1b4239fbc6 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/VSTestProperty.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; + +namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; + +internal sealed class VSTestProperty(TestProperty property, TestCase testCase) : IProperty +{ + public TestProperty Property { get; } = property; + + public TestCase TestCase { get; } = testCase; + + // We don't want to pay the allocation if we're not printing it for instance inside the logs. + // So we go to get the property and ToString() it only if needed. + public override string ToString() + { + object? value = TestCase.GetPropertyValue(Property); + return $"VSTestProperty [Id: {Property.Id}] [Description: {Property.Description}] [ValueType: {Property.ValueType}] [Value: {(value is null ? "null" : value)}]"; + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PACKAGE.md b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PACKAGE.md new file mode 100644 index 0000000000..e11b0ee9a9 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PACKAGE.md @@ -0,0 +1,9 @@ +# Microsoft.Testing + +Microsoft Testing is a set of platform, framework and protocol intended to make it possible to run any test on any target or device. + +Documentation can be found at . + +## About + +This package provides bridge extensions to Microsoft Testing Platform to help test framework authors support both Test Anywhere and the old VSTest Test Platform. diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..65eebdc149 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Shipped.txt @@ -0,0 +1,57 @@ +#nullable enable +abstract Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.SynchronizedDiscoverTestsAsync(Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestDiscoverTestExecutionRequest! request, Microsoft.Testing.Platform.Messages.IMessageBus! messageBus, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +abstract Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.SynchronizedRunTestsAsync(Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest! request, Microsoft.Testing.Platform.Messages.IMessageBus! messageBus, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +abstract Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.CloseTestSessionAsync(Microsoft.Testing.Platform.Extensions.TestFramework.CloseTestSessionContext! context) -> System.Threading.Tasks.Task! +abstract Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.CreateTestSessionAsync(Microsoft.Testing.Platform.Extensions.TestFramework.CreateTestSessionContext! context) -> System.Threading.Tasks.Task! +abstract Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.Description.get -> string! +abstract Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.DiscoverTestsAsync(Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestDiscoverTestExecutionRequest! request, Microsoft.Testing.Platform.Messages.IMessageBus! messageBus, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +abstract Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.DisplayName.get -> string! +abstract Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.IsEnabledAsync() -> System.Threading.Tasks.Task! +abstract Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.RunTestsAsync(Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest! request, Microsoft.Testing.Platform.Messages.IMessageBus! messageBus, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +abstract Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.ExecuteRequestAsync(Microsoft.Testing.Platform.Requests.TestExecutionRequest! request, Microsoft.Testing.Platform.Messages.IMessageBus! messageBus, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +abstract Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.Uid.get -> string! +abstract Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.Version.get -> string! +Microsoft.Testing.Extensions.VSTestBridge.Capabilities.VSTestBridgeExtensionBaseCapabilities +Microsoft.Testing.Extensions.VSTestBridge.Capabilities.VSTestBridgeExtensionBaseCapabilities.IsTrxEnabled.get -> bool +Microsoft.Testing.Extensions.VSTestBridge.Capabilities.VSTestBridgeExtensionBaseCapabilities.VSTestBridgeExtensionBaseCapabilities() -> void +Microsoft.Testing.Extensions.VSTestBridge.Helpers.TestApplicationBuilderExtensions +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestDiscoverTestExecutionRequest +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestDiscoverTestExecutionRequest.AssemblyPaths.get -> string![]! +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestDiscoverTestExecutionRequest.DiscoveryContext.get -> Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IDiscoveryContext! +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestDiscoverTestExecutionRequest.DiscoverySink.get -> Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.ITestCaseDiscoverySink! +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestDiscoverTestExecutionRequest.MessageLogger.get -> Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging.IMessageLogger! +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestDiscoverTestExecutionRequest.VSTestFilter.get -> Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestTestExecutionFilter! +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestDiscoverTestExecutionRequestFactory +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest.AssemblyPaths.get -> string![]! +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest.FrameworkHandle.get -> Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IFrameworkHandle! +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest.RunContext.get -> Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunContext! +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest.VSTestFilter.get -> Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestTestExecutionFilter! +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest.VSTestRunTestExecutionRequest(Microsoft.Testing.Platform.TestHost.TestSessionContext! session, Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestTestExecutionFilter! executionFilter, string![]! assemblyPaths, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunContext! runContext, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IFrameworkHandle! frameworkHandle) -> void +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequestFactory +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestTestExecutionFilter +Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestTestExecutionFilter.TestCases.get -> System.Collections.Immutable.ImmutableArray? +Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework +Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.Dispose() -> void +Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.SynchronizedSingleSessionVSTestBridgedTestFramework(Microsoft.Testing.Platform.Extensions.IExtension! extension, System.Func!>! getTestAssemblies, System.IServiceProvider! serviceProvider, Microsoft.Testing.Platform.Capabilities.TestFramework.ITestFrameworkCapabilities! capabilities) -> void +Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase +Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.DataTypesProduced.get -> System.Type![]! +Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.ExecuteRequestAsync(Microsoft.Testing.Platform.Extensions.TestFramework.ExecuteRequestContext! context) -> System.Threading.Tasks.Task! +Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.IsTrxEnabled.get -> bool +Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.ServiceProvider.get -> System.IServiceProvider! +Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase.VSTestBridgedTestFrameworkBase(System.IServiceProvider! serviceProvider, Microsoft.Testing.Platform.Capabilities.TestFramework.ITestFrameworkCapabilities! capabilities) -> void +override Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.IsEnabledAsync() -> System.Threading.Tasks.Task! +override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.CloseTestSessionAsync(Microsoft.Testing.Platform.Extensions.TestFramework.CloseTestSessionContext! context) -> System.Threading.Tasks.Task! +override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.CreateTestSessionAsync(Microsoft.Testing.Platform.Extensions.TestFramework.CreateTestSessionContext! context) -> System.Threading.Tasks.Task! +override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.Description.get -> string! +override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.DiscoverTestsAsync(Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestDiscoverTestExecutionRequest! request, Microsoft.Testing.Platform.Messages.IMessageBus! messageBus, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.DisplayName.get -> string! +override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.RunTestsAsync(Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest! request, Microsoft.Testing.Platform.Messages.IMessageBus! messageBus, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.ExecuteRequestAsync(Microsoft.Testing.Platform.Requests.TestExecutionRequest! request, Microsoft.Testing.Platform.Messages.IMessageBus! messageBus, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.Uid.get -> string! +override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.Version.get -> string! +static Microsoft.Testing.Extensions.VSTestBridge.Helpers.TestApplicationBuilderExtensions.AddRunSettingsService(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder, Microsoft.Testing.Platform.Extensions.IExtension! extension) -> void +static Microsoft.Testing.Extensions.VSTestBridge.Helpers.TestApplicationBuilderExtensions.AddTestCaseFilterService(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder, Microsoft.Testing.Platform.Extensions.IExtension! extension) -> void +static Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestDiscoverTestExecutionRequestFactory.CreateRequest(Microsoft.Testing.Platform.Requests.DiscoverTestExecutionRequest! discoverTestExecutionRequest, Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase! adapterExtension, string![]! testAssemblyPaths, System.Threading.CancellationToken cancellationToken) -> Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestDiscoverTestExecutionRequest! +static Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequestFactory.CreateRequest(Microsoft.Testing.Platform.Requests.RunTestExecutionRequest! runTestExecutionRequest, Microsoft.Testing.Extensions.VSTestBridge.VSTestBridgedTestFrameworkBase! adapterExtension, string![]! testAssemblyPaths, System.Threading.CancellationToken cancellationToken) -> Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest! +virtual Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.Dispose(bool disposing) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..50e9e00661 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Unshipped.txt @@ -0,0 +1,2 @@ +#nullable enable +static Microsoft.Testing.Extensions.VSTestBridge.Helpers.TestApplicationBuilderExtensions.AddTestRunParametersService(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder, Microsoft.Testing.Platform.Extensions.IExtension! extension) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestDiscoverTestExecutionRequest.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestDiscoverTestExecutionRequest.cs new file mode 100644 index 0000000000..7e737c256b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestDiscoverTestExecutionRequest.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.TestHost; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + +namespace Microsoft.Testing.Extensions.VSTestBridge.Requests; + +/// +/// A specialized discover test execution request for VSTest. It contains the VSTest specific properties. +/// +public sealed class VSTestDiscoverTestExecutionRequest : DiscoverTestExecutionRequest +{ + internal VSTestDiscoverTestExecutionRequest(TestSessionContext session, VSTestTestExecutionFilter executionFilter, string[] assemblyPaths, + IDiscoveryContext discoveryContext, IMessageLogger messageLogger, ITestCaseDiscoverySink discoverySink) + : base(session, executionFilter) + { + AssemblyPaths = assemblyPaths; + DiscoveryContext = discoveryContext; + MessageLogger = messageLogger; + DiscoverySink = discoverySink; + } + + public VSTestTestExecutionFilter VSTestFilter + => (VSTestTestExecutionFilter)Filter; + + public string[] AssemblyPaths { get; } + + public IDiscoveryContext DiscoveryContext { get; } + + public IMessageLogger MessageLogger { get; } + + public ITestCaseDiscoverySink DiscoverySink { get; } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestDiscoverTestExecutionRequestFactory.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestDiscoverTestExecutionRequestFactory.cs new file mode 100644 index 0000000000..946606fe3c --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestDiscoverTestExecutionRequestFactory.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.VSTestBridge.Capabilities; +using Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.Services; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + +namespace Microsoft.Testing.Extensions.VSTestBridge.Requests; + +/// +/// VSTest specific implementation of Microsoft Testing Platform . +/// +public sealed class VSTestDiscoverTestExecutionRequestFactory : ITestExecutionRequestFactory +{ + private readonly ITestFrameworkCapabilities _testFrameworkCapabilities; + private readonly ITestFramework _testFrameworkAdapter; + private readonly ICommandLineOptions _commandLineService; + private readonly ILogger _logger; + private readonly string[] _assemblyPaths; + private readonly VSTestTestExecutionFilter _testExecutionFilter; + private readonly IDiscoveryContext _discoveryContext; + private readonly IMessageLogger _messageLogger; + private readonly ITestCaseDiscoverySink _discoverySink; + + internal VSTestDiscoverTestExecutionRequestFactory(ITestFrameworkCapabilities testFrameworkCapabilities, ITestFramework testFrameworkAdapter, + ICommandLineOptions commandLineService, ILoggerFactory loggerFactory, string[] assemblyPaths, VSTestTestExecutionFilter testExecutionFilter, + IDiscoveryContext discoveryContext, IMessageLogger messageLogger, ITestCaseDiscoverySink discoverySink) + { + _testFrameworkCapabilities = testFrameworkCapabilities; + _testFrameworkAdapter = testFrameworkAdapter; + _commandLineService = commandLineService; + _logger = loggerFactory.CreateLogger(); + _assemblyPaths = assemblyPaths; + _discoveryContext = discoveryContext; + _messageLogger = messageLogger; + _discoverySink = discoverySink; + _testExecutionFilter = testExecutionFilter; + } + + Task ITestExecutionRequestFactory.CreateRequestAsync(Platform.TestHost.TestSessionContext session) + => !_commandLineService.IsOptionSet(PlatformCommandLineProvider.VSTestAdapterModeOptionKey) + ? throw new InvalidOperationException($"Command line argument {PlatformCommandLineProvider.VSTestAdapterModeOptionKey} is not set but we are in VSTest adapter mode. This is a bug in the adapter.") + : _testFrameworkCapabilities.GetCapability()?.IsSupported != true + ? throw new InvalidOperationException($"Skipping test adapter {_testFrameworkAdapter.DisplayName} because it is not {nameof(IVSTestFlattenedTestNodesReportCapability)} capable.") + : !_commandLineService.IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey) + ? throw new NotSupportedException($"The {nameof(VSTestRunTestExecutionRequestFactory)} does not support creating a {nameof(DiscoverTestExecutionRequest)}.") + : Task.FromResult(new VSTestDiscoverTestExecutionRequest(session, _testExecutionFilter, _assemblyPaths, _discoveryContext, _messageLogger, _discoverySink)); + + public static VSTestDiscoverTestExecutionRequest CreateRequest( + DiscoverTestExecutionRequest discoverTestExecutionRequest, + VSTestBridgedTestFrameworkBase adapterExtension, string[] testAssemblyPaths, CancellationToken cancellationToken) + { + IServiceProvider serviceProvider = adapterExtension.ServiceProvider; + IConfiguration configuration = serviceProvider.GetConfiguration(); + ILoggerFactory loggerFactory = serviceProvider.GetRequiredService(); + IFileSystem fileSystem = serviceProvider.GetFileSystem(); + + ICommandLineOptions commandLineOptions = serviceProvider.GetRequiredService(); + RunSettingsAdapter runSettings = new(commandLineOptions, fileSystem, configuration, discoverTestExecutionRequest.Session.Client, loggerFactory); + DiscoveryContextAdapter discoveryContext = new(commandLineOptions, runSettings); + + IOutputDevice outputDevice = serviceProvider.GetOutputDevice(); + MessageLoggerAdapter messageLogger = new(loggerFactory, outputDevice, adapterExtension); + + ITestApplicationModuleInfo testApplicationModuleInfo = serviceProvider.GetTestApplicationModuleInfo(); + IMessageBus messageBus = serviceProvider.GetRequiredService(); + TestCaseDiscoverySinkAdapter discoverySink = new(adapterExtension, discoverTestExecutionRequest.Session, testAssemblyPaths, testApplicationModuleInfo, loggerFactory, messageBus, adapterExtension.IsTrxEnabled, cancellationToken); + + return new(discoverTestExecutionRequest.Session, new(), testAssemblyPaths, discoveryContext, messageLogger, discoverySink); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestRunTestExecutionRequest.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestRunTestExecutionRequest.cs new file mode 100644 index 0000000000..c0d52e8cb9 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestRunTestExecutionRequest.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.TestHost; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + +namespace Microsoft.Testing.Extensions.VSTestBridge.Requests; + +/// +/// A specialized run test execution request for VSTest. It contains the VSTest specific properties. +/// +public sealed class VSTestRunTestExecutionRequest : RunTestExecutionRequest +{ + public VSTestRunTestExecutionRequest(TestSessionContext session, VSTestTestExecutionFilter executionFilter, string[] assemblyPaths, + IRunContext runContext, IFrameworkHandle frameworkHandle) + : base(session, executionFilter) + { + AssemblyPaths = assemblyPaths; + RunContext = runContext; + FrameworkHandle = frameworkHandle; + } + + public VSTestTestExecutionFilter VSTestFilter + => (VSTestTestExecutionFilter)Filter; + + public string[] AssemblyPaths { get; } + + public IRunContext RunContext { get; } + + public IFrameworkHandle FrameworkHandle { get; } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestRunTestExecutionRequestFactory.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestRunTestExecutionRequestFactory.cs new file mode 100644 index 0000000000..dfd5d83b9a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestRunTestExecutionRequestFactory.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.VSTestBridge.Capabilities; +using Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.Services; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + +namespace Microsoft.Testing.Extensions.VSTestBridge.Requests; + +/// +/// VSTest specific implementation of Microsoft Testing Platform . +/// +public sealed class VSTestRunTestExecutionRequestFactory : ITestExecutionRequestFactory +{ + private readonly ITestFrameworkCapabilities _testFrameworkCapabilities; + private readonly ITestFramework _testFrameworkAdapter; + private readonly ICommandLineOptions _commandLineService; + private readonly ILogger _logger; + private readonly string[] _assemblyPaths; + private readonly VSTestTestExecutionFilter _testExecutionFilter; + private readonly IRunContext _runContext; + private readonly IFrameworkHandle _frameworkHandle; + + internal VSTestRunTestExecutionRequestFactory(ITestFrameworkCapabilities testFrameworkCapabilities, ITestFramework testFrameworkAdapter, + ICommandLineOptions commandLineService, ILoggerFactory loggerFactory, string[] assemblyPaths, VSTestTestExecutionFilter testExecutionFilter, + IRunContext runContext, IFrameworkHandle frameworkHandle) + { + _testFrameworkCapabilities = testFrameworkCapabilities; + _testFrameworkAdapter = testFrameworkAdapter; + _commandLineService = commandLineService; + _logger = loggerFactory.CreateLogger(); + _assemblyPaths = assemblyPaths; + _testExecutionFilter = testExecutionFilter; + _runContext = runContext; + _frameworkHandle = frameworkHandle; + } + + Task ITestExecutionRequestFactory.CreateRequestAsync(Platform.TestHost.TestSessionContext session) + => !_commandLineService.IsOptionSet(PlatformCommandLineProvider.VSTestAdapterModeOptionKey) + ? throw new InvalidOperationException($"Command line argument {PlatformCommandLineProvider.VSTestAdapterModeOptionKey} is not set but we are in VSTest adapter mode. This is a bug in the adapter.") + : _testFrameworkCapabilities.GetCapability()?.IsSupported != true + ? throw new InvalidOperationException($"Skipping test adapter {_testFrameworkAdapter.DisplayName} because it is not {nameof(IVSTestFlattenedTestNodesReportCapability)} capable.") + : _commandLineService.IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey) + ? throw new NotSupportedException($"The {nameof(VSTestRunTestExecutionRequestFactory)} does not support creating a {nameof(DiscoverTestExecutionRequest)}.") + : Task.FromResult(new VSTestRunTestExecutionRequest(session, _testExecutionFilter, _assemblyPaths, _runContext, _frameworkHandle)); + + /// + /// Helper method to convert a to a . + /// + /// The request to convert. + /// The test adapter extension. + /// The list of assemblies to test. + /// A cancellation token. + public static VSTestRunTestExecutionRequest CreateRequest( + RunTestExecutionRequest runTestExecutionRequest, + VSTestBridgedTestFrameworkBase adapterExtension, + string[] testAssemblyPaths, + CancellationToken cancellationToken) + { + IServiceProvider serviceProvider = adapterExtension.ServiceProvider; + IConfiguration configuration = serviceProvider.GetConfiguration(); + ICommandLineOptions commandLineOptions = serviceProvider.GetRequiredService(); + ILoggerFactory loggerFactory = serviceProvider.GetRequiredService(); + IFileSystem fileSystem = serviceProvider.GetFileSystem(); + + RunSettingsAdapter runSettings = new(commandLineOptions, fileSystem, configuration, runTestExecutionRequest.Session.Client, loggerFactory); + RunContextAdapter runContext = runTestExecutionRequest.Filter is TestNodeUidListFilter uidListFilter + ? new(commandLineOptions, runSettings, uidListFilter.TestNodeUids) + : new(commandLineOptions, runSettings); + + ITestApplicationModuleInfo testApplicationModuleInfo = serviceProvider.GetTestApplicationModuleInfo(); + IMessageBus messageBus = serviceProvider.GetRequiredService(); + IOutputDevice outputDevice = serviceProvider.GetOutputDevice(); + FrameworkHandlerAdapter frameworkHandlerAdapter = new(adapterExtension, runTestExecutionRequest.Session, testAssemblyPaths, + testApplicationModuleInfo, loggerFactory, messageBus, outputDevice, adapterExtension.IsTrxEnabled, cancellationToken); + + return new(runTestExecutionRequest.Session, new(), testAssemblyPaths, runContext, frameworkHandlerAdapter); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestTestExecutionFilter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestTestExecutionFilter.cs new file mode 100644 index 0000000000..68be9d37d5 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Requests/VSTestTestExecutionFilter.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Microsoft.Testing.Platform.Requests; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; + +namespace Microsoft.Testing.Extensions.VSTestBridge.Requests; + +/// +/// A specialized test execution filter for VSTest. It contains the VSTest specific properties. +/// +public sealed class VSTestTestExecutionFilter : ITestExecutionFilter +{ + internal VSTestTestExecutionFilter() + { + } + + internal VSTestTestExecutionFilter(ImmutableArray testCases) + { + TestCases = testCases; + } + + public ImmutableArray? TestCases { get; } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/ExtensionResources.Designer.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/ExtensionResources.Designer.cs new file mode 100644 index 0000000000..d9a86127d5 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/ExtensionResources.Designer.cs @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Testing.Extensions.VSTestBridge.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ExtensionResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ExtensionResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Testing.Extensions.VSTestBridge.Resources.ExtensionResources", typeof(ExtensionResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to runsettings file '{0}' cannot be read. + /// + internal static string RunsettingsFileCannotBeRead { + get { + return ResourceManager.GetString("RunsettingsFileCannotBeRead", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to runsettings file '{0}' does not exist. + /// + internal static string RunsettingsFileDoesNotExist { + get { + return ResourceManager.GetString("RunsettingsFileDoesNotExist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file. + /// + internal static string RunSettingsOptionDescription { + get { + return ResourceManager.GetString("RunSettingsOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests.. + /// + internal static string TestCaseFilterOptionDescription { + get { + return ResourceManager.GetString("TestCaseFilterOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'.. + /// + internal static string TestRunParameterOptionArgumentIsNotParameter { + get { + return ResourceManager.GetString("TestRunParameterOptionArgumentIsNotParameter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters. + /// + internal static string TestRunParameterOptionDescription { + get { + return ResourceManager.GetString("TestRunParameterOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The bridged framework supports only one session at a time. A session with UID {0} is already open.. + /// + internal static string VSTestBridgedTestFrameworkSessionAlreadyCreatedErrorMessage { + get { + return ResourceManager.GetString("VSTestBridgedTestFrameworkSessionAlreadyCreatedErrorMessage", resourceCulture); + } + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/ExtensionResources.resx b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/ExtensionResources.resx new file mode 100644 index 0000000000..8a15a99eba --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/ExtensionResources.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + runsettings file '{0}' cannot be read + + + runsettings file '{0}' does not exist + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.cs.xlf new file mode 100644 index 0000000000..c97e29b26a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.cs.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + Relativní nebo absolutní cesta k souboru .runsettings. Další informace a příklady konfigurace testovacího běhu najdete v tématu https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + + + + runsettings file '{0}' cannot be read + Soubor runsettings „{0}“ nejde přečíst + + + + runsettings file '{0}' does not exist + Soubor runsettings „{0}“ neexistuje + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + Filtruje testy pomocí daného výrazu. Další informace najdete v části s podrobnostmi o možnosti filtru. Další informace a příklady použití selektivního filtrování testů jednotek najdete v tématu https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + argument {0} není parametr. Argumenty parametrů odpovídají následujícímu vzoru key=value (klíč=hodnota). + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + Zadejte nebo přepište parametr páru klíč-hodnota. Další informace a příklady najdete tady: https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + Přemostěná architektura podporuje najednou pouze jednu relaci. Relace s UID {0} je už otevřená. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.de.xlf new file mode 100644 index 0000000000..be20f5cd52 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.de.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + Der relative oder absolute Pfad zur Datei „.runsettings“. Weitere Informationen und Beispiele zum Konfigurieren des Testlaufs finden Sie unter https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + + + + runsettings file '{0}' cannot be read + Runsettings-Datei „{0}“ kann nicht gelesen werden. + + + + runsettings file '{0}' does not exist + Runsettings-Datei „{0}“ ist nicht vorhanden. + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + Filtert Tests mithilfe des angegebenen Ausdrucks. Weitere Informationen finden Sie im Abschnitt „Filteroptionsdetails“. Weitere Informationen und Beispiele zur Verwendung der selektiven Komponententestfilterung finden Sie unter https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + Das Argument „{0}“ ist kein Parameter. Parameterargumente stimmen mit dem folgenden Muster „key=value“ überein. + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + Geben Sie einen Schlüssel-Wert-Paarparameter an, oder überschreiben Sie diesen. Weitere Informationen und Beispiele finden Sie unter https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + Das Brückenframework unterstützt jeweils nur eine Sitzung. Eine Sitzung mit UID {0} ist bereits geöffnet. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.es.xlf new file mode 100644 index 0000000000..361b202ea9 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.es.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + Ruta de acceso, relativa o absoluta, al archivo .runsettings. Para obtener más información y ejemplos sobre cómo configurar la serie de pruebas, consulte https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + + + + runsettings file '{0}' cannot be read + No se puede leer el archivo '{0}' de runsettings + + + + runsettings file '{0}' does not exist + El archivo '{0}' de runsettings no existe + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + Filtra las pruebas mediante la expresión especificada. Para obtener más información, consulte la sección Detalles de la opción Filtrar. Para obtener más información y ejemplos sobre cómo usar el filtrado de pruebas unitarias selectivas, consulte https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + el argumento "{0}" no es un parámetro. Los argumentos de parámetros coinciden con el patrón "clave=valor" siguiente. + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + Especifique o invalide un parámetro de par clave-valor. Para más información y ejemplos, consulte https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + El marco puente solo admite una sesión a la vez. Ya hay abierta una sesión con {0} UID. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.fr.xlf new file mode 100644 index 0000000000..27667e618a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.fr.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + Chemin d’accès, relatif ou absolu, au fichier .runsettings. Pour plus d’informations et d’exemples sur la configuration de la série de tests, consultez https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + + + + runsettings file '{0}' cannot be read + impossible de lire le fichier runsettings « {0} » + + + + runsettings file '{0}' does not exist + Le fichier .runsettings n’existe pas sur « {0} » + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + Filtre les tests à l’aide de l’expression donnée. Pour plus d’informations, consultez la section des détails de l’option Filtrer. Pour plus d’informations et d’exemples sur l’utilisation du filtrage sélectif des tests unitaires, consultez https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + l’argument « {0} » n’est pas un paramètre. Les arguments de paramètres correspondent au modèle suivant « key=value ». + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + Spécifiez ou remplacez un paramètre de paire clé-valeur. Pour découvrir plus d’informations et d’exemples, voir https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + Le framework ponté ne prend en charge qu’une seule session à la fois. Une session avec UID {0} est déjà ouverte. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.it.xlf new file mode 100644 index 0000000000..52cd99726e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.it.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + Percorso, relativo o assoluto, del file con estensione runsettings. Per altre informazioni ed esempi su come configurare l'esecuzione dei test, vedere https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + + + + runsettings file '{0}' cannot be read + Non è possibile leggere il file runsettings '{0}' + + + + runsettings file '{0}' does not exist + Il file runsettings '{0}' non esiste + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + Filtra i test usando l'espressione specificata. Per ulteriori informazioni, vedere la sezione dei dettagli dell'opzione Filtro. Per altre informazioni ed esempi su come usare il filtro selettivo unit test, vedere https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + l'argomento '{0}' non è un parametro. Gli argomenti dei parametri corrispondono al criterio 'key=value' seguente. + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + Specificare o eseguire l'override di un parametro di coppia chiave-valore. Per altre informazioni ed esempi, vedere https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + Il framework bridged supporta solo una sessione alla volta. Una sessione con UID {0} è già aperta. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.ja.xlf new file mode 100644 index 0000000000..2d20ee3e93 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.ja.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + .runsettings ファイルへの相対パスまたは絶対パス。テストの実行を構成する方法の詳細と例については、https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file を参照してください + + + + runsettings file '{0}' cannot be read + runsettings ファイル '{0}' を読み取れません + + + + runsettings file '{0}' does not exist + runsettings ファイル '{0}' が存在しません + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + 指定された式を使用してテストをフィルター処理します。詳細については、「フィルター オプションの詳細」セクションを参照してください。選択的単体テスト フィルターの使用方法の詳細と例については、https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests を参照してください。 + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + 引数 '{0}' はパラメーターではありません。パラメーター引数は、次のパターン 'key=value' と一致しています。 + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + キーと値のペア パラメーターを指定またはオーバーライドします。詳細情報と例については、「https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters」を参照してください。 + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + ブリッジ されたフレームワークは、一度に 1 つのセッションのみをサポートします。UID{0} のセッションは既に開かれています。 + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.ko.xlf new file mode 100644 index 0000000000..53f138a21a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.ko.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + .runsettings 파일에 대한 상대 또는 절대 경로입니다. 테스트 실행을 구성하는 방법에 관한 자세한 내용 및 예시는 https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file을 참조하세요. + + + + runsettings file '{0}' cannot be read + runsettings 파일 '{0}'을(를) 읽을 수 없습니다. + + + + runsettings file '{0}' does not exist + runsettings 파일 '{0}'이(가) 없습니다. + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + 지정된 식을 사용하여 테스트를 필터링합니다. 자세한 내용은 필터 옵션 세부 정보 섹션을 참조하세요. 선택적 단위 테스트 필터링을 사용하는 방법에 관한 자세한 내용과 예시는 https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests를 참조하세요. + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + 인수 '{0}'은(는) 매개 변수가 아닙니다. 매개 변수 인수가 다음 패턴 '키=값'과 일치합니다. + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + 키-값 쌍 매개 변수를 지정하거나 재정의합니다. 자세한 내용 및 예제는 https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters를 참조하세요. + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + 브리지된 프레임워크는 한 번에 하나의 세션만 지원합니다. UID가 {0}인 세션이 이미 열려 있습니다. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.pl.xlf new file mode 100644 index 0000000000..ac273ca6dd --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.pl.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + Ścieżka względna lub bezwzględna pliku .runsettings. Aby uzyskać więcej informacji i przykładów dotyczących konfigurowania przebiegu testu, zobacz: https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + + + + runsettings file '{0}' cannot be read + nie można odczytać pliku runsettings „{0}” + + + + runsettings file '{0}' does not exist + plik runsettings „{0}” nie istnieje + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + Filtruje testy przy użyciu danego wyrażenia. Aby uzyskać więcej informacji, zobacz sekcję szczegółów opcji filtru. Aby uzyskać więcej informacji i przykładów dotyczących używania selektywnego filtrowania testów jednostkowych, zobacz: https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + argument „{0}” nie jest parametrem. Argumenty parametrów pasują do następującego wzorca „klucz=wartość”. + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + Określ lub zastąp parametr pary klucz-wartość. Aby uzyskać więcej informacji i przykładów, zobacz stronę https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + Platforma mostkowania obsługuje tylko jedną sesję naraz. Sesja z identyfikatorem UID {0} jest już otwarta. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.pt-BR.xlf new file mode 100644 index 0000000000..94f773017f --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.pt-BR.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + O caminho, relativo ou absoluto, para o arquivo .runsettings. Para obter mais informações e exemplos sobre como configurar a execução de teste, consulte https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + + + + runsettings file '{0}' cannot be read + o arquivo runsettings "{0}" não pode ser lido + + + + runsettings file '{0}' does not exist + o arquivo runsettings "{0}" não existe + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + Filtra testes usando a expressão fornecida. Para obter mais informações, consulte a seção detalhes da opção Filtro. Para obter mais informações e exemplos sobre como usar a filtragem de teste de unidade seletiva, consulte https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + o argumento '{0}' não é um parâmetro. Os argumentos de parâmetros correspondem ao padrão 'key=value' a seguir. + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + Especifique ou substitua um parâmetro de par chave-valor. Para obter mais informações e exemplos, consulte https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + A estrutura em ponte dá suporte a apenas uma sessão por vez. Uma sessão com UID {0} já está aberta. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.ru.xlf new file mode 100644 index 0000000000..4e8d17776b --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.ru.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + Относительный или абсолютный путь к файлу .runsettings. Дополнительные сведения и примеры настройки тестового запуска см. на странице https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + + + + runsettings file '{0}' cannot be read + не удается прочитать файл настроек запуска "{0}" + + + + runsettings file '{0}' does not exist + файл настроек запуска "{0}" не существует + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + Фильтрация тестов с использованием заданного выражения. Дополнительные сведения см. в разделе сведений о параметрах фильтра. Дополнительные сведения и примеры использования выборочной фильтрации модульных тестов см. на странице https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + аргумент "{0}" не является параметром. Аргументы параметров соответствуют следующему шаблону: "ключ=значение". + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + Укажите или переопределите параметр пары "ключ-значение". Дополнительные сведения и примеры см. на странице https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + Интегрированная среда Bridged Framework поддерживает только один сеанс одновременно. Сеанс с идентификатором пользователя {0} уже открыт. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.tr.xlf new file mode 100644 index 0000000000..ca52496a11 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.tr.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + .runsettings dosyasının göreli veya mutlak yolu. Test çalıştırmasını yapılandırma hakkında daha fazla bilgi ve örnek için şu sayfaya bakın: https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + + + + runsettings file '{0}' cannot be read + '{0}' runsettings dosyası okunamıyor + + + + runsettings file '{0}' does not exist + '{0}' runsettings dosyası mevcut değil + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + Belirtilen ifadeyi kullanarak testleri filtreler. Daha fazla bilgi için Filtre seçeneği ayrıntıları bölümüne bakın. Seçici birim testi filtreleme hakkında daha fazla bilgi ve örnek için şu sayfaya bakın: https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + '{0}' bağımsız değişkeni bir parametre değil. Parametre bağımsız değişkenleri aşağıdaki 'key=value' deseniyle eşleşiyor. + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + Anahtar-değer çifti parametresi belirtin veya geçersiz kılın. Daha fazla bilgi ve örnek için şu sayfaya bakın: https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + Köprülü çerçeve aynı anda yalnızca bir oturumu destekler. UID'si {0} olan bir oturum zaten açık. + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.zh-Hans.xlf new file mode 100644 index 0000000000..b029a8c595 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.zh-Hans.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + .runsettings 文件的相对路径或绝对路径。有关如何配置测试运行的详细信息和示例,请参阅 https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + + + + runsettings file '{0}' cannot be read + 无法读取 runsettings 文件“{0}” + + + + runsettings file '{0}' does not exist + runsettings 文件“{0}”不存在 + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + 使用给定的表达式筛选测试。有关详细信息,请参阅“筛选器选项详细信息”部分。有关如何使用选择性单元测试筛选的详细信息和示例,请参阅 https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests。 + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + 自变量“{0}”不是参数。参数自变量与以下模式“key=value”相匹配。 + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + 指定或替代键值对参数。有关详细信息和示例,请参阅 https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + 桥接框架一次仅支持一个会话。已打开具有 UID {0} 的会话。 + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.zh-Hant.xlf new file mode 100644 index 0000000000..1946ae1c88 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Resources/xlf/ExtensionResources.zh-Hant.xlf @@ -0,0 +1,42 @@ + + + + + + The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + .runsettings 檔案的相對或絕對路徑。如需如何設定測試回合的詳細資訊和範例,請參閱 https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + + + + runsettings file '{0}' cannot be read + 無法讀取 runsettings 檔案 '{0}' + + + + runsettings file '{0}' does not exist + .runsettings 檔案 '{0}' 不存在 + + + + Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + 使用指定運算式的篩選測試。如需詳細資訊,請參閱篩選選項詳細資料區段。如需如何使用選擇性單元測試篩選的詳細資訊和範例,請參閱 https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests。 + + + + argument '{0}' is not a parameter. Parameters arguments are matching the following pattern 'key=value'. + 引數 '{0}' 不是參數。參數引數符合下列模式 'key=value'。 + + + + Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + 指定或覆寫機碼值組參數。如需詳細資訊和範例,請參閱 https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + + + + The bridged framework supports only one session at a time. A session with UID {0} is already open. + 橋接器架構一次只支援一個工作階段。已開啟具有 UID {0} 的工作階段。 + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/SynchronizedSingleSessionVSTestAndTestAnywhereAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/SynchronizedSingleSessionVSTestAndTestAnywhereAdapter.cs new file mode 100644 index 0000000000..9f207d3a51 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/SynchronizedSingleSessionVSTestAndTestAnywhereAdapter.cs @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; +using System.Reflection; + +using Microsoft.Testing.Extensions.VSTestBridge.Requests; +using Microsoft.Testing.Extensions.VSTestBridge.Resources; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.TestHost; + +namespace Microsoft.Testing.Extensions.VSTestBridge; + +public abstract class SynchronizedSingleSessionVSTestBridgedTestFramework : VSTestBridgedTestFrameworkBase, IDisposable +{ + private readonly IExtension _extension; + private readonly Func> _getTestAssemblies; + private readonly CountdownEvent _incomingRequestCounter = new(1); + private bool _isDisposed; + private SessionUid? _sessionUid; + + protected SynchronizedSingleSessionVSTestBridgedTestFramework(IExtension extension, Func> getTestAssemblies, + IServiceProvider serviceProvider, ITestFrameworkCapabilities capabilities) + : base(serviceProvider, capabilities) + { + _extension = extension; + _getTestAssemblies = getTestAssemblies; + } + + /// + public sealed override string Uid => _extension.Uid; + + /// + public sealed override string DisplayName => _extension.DisplayName; + + /// + public sealed override string Description => _extension.Description; + + /// + public sealed override string Version => _extension.Version; + + /// + public override async Task IsEnabledAsync() => await _extension.IsEnabledAsync(); + + /// + public sealed override Task CreateTestSessionAsync(CreateTestSessionContext context) + { + context.CancellationToken.ThrowIfCancellationRequested(); + + if (_sessionUid is not null) + { + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, ExtensionResources.VSTestBridgedTestFrameworkSessionAlreadyCreatedErrorMessage, _sessionUid.Value.Value)); + } + + _sessionUid = context.SessionUid; + return Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + } + + /// + public sealed override async Task CloseTestSessionAsync(CloseTestSessionContext context) + { + // Clear initial count + _incomingRequestCounter.Signal(); + + // Wait for remaining request processing + await _incomingRequestCounter.WaitAsync(context.CancellationToken); + _sessionUid = null; + return new CloseTestSessionResult() { IsSuccess = true }; + } + + protected virtual void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + { + // Dispose managed state (managed objects) + _incomingRequestCounter.Dispose(); + } + + // Free unmanaged resources (unmanaged objects) and override finalizer + // Set large fields to null + _isDisposed = true; + } + } + + protected sealed override Task DiscoverTestsAsync(VSTestDiscoverTestExecutionRequest request, IMessageBus messageBus, + CancellationToken cancellationToken) + => ExecuteRequestWithRequestCountGuardAsync(async () => await SynchronizedDiscoverTestsAsync(request, messageBus, cancellationToken)); + + protected abstract Task SynchronizedDiscoverTestsAsync(VSTestDiscoverTestExecutionRequest request, IMessageBus messageBus, + CancellationToken cancellationToken); + + protected sealed override Task RunTestsAsync(VSTestRunTestExecutionRequest request, IMessageBus messageBus, + CancellationToken cancellationToken) + => ExecuteRequestWithRequestCountGuardAsync(async () => await SynchronizedRunTestsAsync(request, messageBus, cancellationToken)); + + protected abstract Task SynchronizedRunTestsAsync(VSTestRunTestExecutionRequest request, IMessageBus messageBus, + CancellationToken cancellationToken); + + protected sealed override Task ExecuteRequestAsync(TestExecutionRequest request, IMessageBus messageBus, + CancellationToken cancellationToken) + => ExecuteRequestWithRequestCountGuardAsync(async () => + { +#pragma warning disable IL3000 // Avoid accessing Assembly file path when publishing as a single file + string[] testAssemblyPaths = _getTestAssemblies().Select(x => x.Location).ToArray(); +#pragma warning restore IL3000 // Avoid accessing Assembly file path when publishing as a single file + switch (request) + { + case DiscoverTestExecutionRequest discoverRequest: + VSTestDiscoverTestExecutionRequest vstestDiscoverRequest = + VSTestDiscoverTestExecutionRequestFactory.CreateRequest(discoverRequest, this, testAssemblyPaths, cancellationToken); + await SynchronizedDiscoverTestsAsync(vstestDiscoverRequest, messageBus, cancellationToken); + break; + + case RunTestExecutionRequest runRequest: + VSTestRunTestExecutionRequest vstestRunRequest = + VSTestRunTestExecutionRequestFactory.CreateRequest(runRequest, this, testAssemblyPaths, cancellationToken); + await SynchronizedRunTestsAsync(vstestRunRequest, messageBus, cancellationToken); + break; + + default: + throw new NotSupportedException($"VSTest Test Adapters do not support requests of type '{request.GetType()}'."); + } + }); + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + private async Task ExecuteRequestWithRequestCountGuardAsync(Func asyncFunc) + { + _incomingRequestCounter.AddCount(); + + try + { + await asyncFunc(); + } + finally + { + _incomingRequestCounter.Signal(); + } + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/VSTestBridgedTestFrameworkBase.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/VSTestBridgedTestFrameworkBase.cs new file mode 100644 index 0000000000..5c679f7ca5 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/VSTestBridgedTestFrameworkBase.cs @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.TrxReport.Abstractions; +using Microsoft.Testing.Extensions.VSTestBridge.Helpers; +using Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; +using Microsoft.Testing.Extensions.VSTestBridge.Requests; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Extensions.VSTestBridge; + +public abstract class VSTestBridgedTestFrameworkBase : ITestFramework, IDataProducer +{ + protected VSTestBridgedTestFrameworkBase(IServiceProvider serviceProvider, ITestFrameworkCapabilities capabilities) + { + ArgumentGuard.IsNotNull(serviceProvider); + ServiceProvider = serviceProvider; + IsTrxEnabled = capabilities.GetCapability()?.IsSupported == true; + } + + /// + public abstract string Uid { get; } + + /// + public abstract string Version { get; } + + /// + public abstract string DisplayName { get; } + + /// + public abstract string Description { get; } + + /// + public Type[] DataTypesProduced { get; } = + [ + typeof(TestNodeUpdateMessage), + typeof(SessionFileArtifact), + typeof(TestNodeFileArtifact) + ]; + + protected internal IServiceProvider ServiceProvider { get; } + + protected internal bool IsTrxEnabled { get; } + + /// + public abstract Task IsEnabledAsync(); + + /// + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + try + { + DebugUtils.LaunchAttachDebugger(); + + Task convertedRequest = context.Request switch + { + VSTestDiscoverTestExecutionRequest discoverRequest => + DiscoverTestsAsync(UpdateDiscoverRequest(discoverRequest, context.MessageBus, context.CancellationToken), context.MessageBus, context.CancellationToken), + + VSTestRunTestExecutionRequest runRequest => + RunTestsAsync(UpdateRunRequest(runRequest, context.MessageBus, context.CancellationToken), context.MessageBus, context.CancellationToken), + + TestExecutionRequest request => ExecuteRequestAsync(request, context.MessageBus, context.CancellationToken), + + _ => Task.CompletedTask, + }; + + await convertedRequest; + } + finally + { + // Complete the TA request. + context.Complete(); + } + } + + /// + public abstract Task CreateTestSessionAsync(CreateTestSessionContext context); + + /// + public abstract Task CloseTestSessionAsync(CloseTestSessionContext context); + + protected abstract Task ExecuteRequestAsync(TestExecutionRequest request, IMessageBus messageBus, + CancellationToken cancellationToken); + + protected abstract Task DiscoverTestsAsync(VSTestDiscoverTestExecutionRequest request, IMessageBus messageBus, + CancellationToken cancellationToken); + + protected abstract Task RunTestsAsync(VSTestRunTestExecutionRequest request, IMessageBus messageBus, + CancellationToken cancellationToken); + + private VSTestDiscoverTestExecutionRequest UpdateDiscoverRequest( + VSTestDiscoverTestExecutionRequest discoverRequest, + IMessageBus messageBus, CancellationToken cancellationToken) + { + // Before passing down the request, we need to replace the discovery sink with a custom implementation calling + // both the original (VSTest) sink and our own. + ITestApplicationModuleInfo testApplicationModuleInfo = ServiceProvider.GetTestApplicationModuleInfo(); + ILoggerFactory loggerFactory = ServiceProvider.GetRequiredService(); + TestCaseDiscoverySinkAdapter testCaseDiscoverySinkAdapter = new(this, discoverRequest.Session, discoverRequest.AssemblyPaths, testApplicationModuleInfo, loggerFactory, messageBus, IsTrxEnabled, cancellationToken, discoverRequest.DiscoverySink); + + return new(discoverRequest.Session, discoverRequest.VSTestFilter, discoverRequest.AssemblyPaths, discoverRequest.DiscoveryContext, + discoverRequest.MessageLogger, testCaseDiscoverySinkAdapter); + } + + private VSTestRunTestExecutionRequest UpdateRunRequest(VSTestRunTestExecutionRequest runRequest, IMessageBus messageBus, + CancellationToken cancellationToken) + { + // Before passing down the request, we need to replace the framework handle with a custom implementation calling + // both the original (VSTest) framework handle and our own. + ITestApplicationModuleInfo testApplicationModuleInfo = ServiceProvider.GetTestApplicationModuleInfo(); + ILoggerFactory loggerFactory = ServiceProvider.GetRequiredService(); + IOutputDevice outputDevice = ServiceProvider.GetOutputDevice(); + FrameworkHandlerAdapter frameworkHandlerAdapter = new(this, runRequest.Session, runRequest.AssemblyPaths, testApplicationModuleInfo, + loggerFactory, messageBus, outputDevice, IsTrxEnabled, cancellationToken, runRequest.FrameworkHandle); + + return new(runRequest.Session, runRequest.VSTestFilter, runRequest.AssemblyPaths, runRequest.RunContext, + frameworkHandlerAdapter); + } +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/FileSystem.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/FileSystem.cs new file mode 100644 index 0000000000..b6d252fd6a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/FileSystem.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform.MSBuild; + +internal sealed class FileSystem : IFileSystem +{ + public void CopyFile(string source, string destination) + { + using FileStream fileRead = File.OpenRead(source); + using FileStream fs = new(destination, FileMode.Create); + { + fileRead.CopyTo(fs); + } + } + + public void CreateDirectory(string directory) => Directory.CreateDirectory(directory); + + public Stream CreateNew(string path) => new FileStream(path, FileMode.Create); + + public bool Exist(string path) => File.Exists(path); + + public void WriteAllText(string path, string? contents) => File.WriteAllText(path, contents); +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/IFileSystem.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/IFileSystem.cs new file mode 100644 index 0000000000..5ef7de2feb --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/IFileSystem.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform.MSBuild; + +internal interface IFileSystem +{ + bool Exist(string path); + + void CopyFile(string source, string destination); + + void WriteAllText(string path, string? contents); + + Stream CreateNew(string path); + + void CreateDirectory(string directory); +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Microsoft.Testing.Platform.MSBuild.csproj b/src/Platform/Microsoft.Testing.Platform.MSBuild/Microsoft.Testing.Platform.MSBuild.csproj new file mode 100644 index 0000000000..d4e42bb67a --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Microsoft.Testing.Platform.MSBuild.csproj @@ -0,0 +1,105 @@ + + + $(MicrosoftTestingTargetFrameworks);netstandard2.0 + PLATFORM_MSBUILD + $(NoWarn);NU5100 + + + + + + + + + + + + + + + + + + + + + + + + + + + true + buildMultiTargeting + + + buildTransitive/$(TargetFramework) + + + build/$(TargetFramework) + + + + + + $(TargetsForTfmSpecificBuildOutput);CustomContentTarget + + + + + + _MSBuildTasks/$(TargetFramework) + + + _MSBuildTasks/$(TargetFramework) + + + + + + + True + True + MSBuildResources.resx + + + + + ResXFileCodeGenerator + MSBuildResources.Designer.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/PACKAGE.md b/src/Platform/Microsoft.Testing.Platform.MSBuild/PACKAGE.md new file mode 100644 index 0000000000..145cfef903 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/PACKAGE.md @@ -0,0 +1,9 @@ +# Microsoft.Testing + +Microsoft Testing is a set of platform, framework and protocol intended to make it possible to run any test on any target or device. + +Documentation can be found at . + +## About + +This package provides MSBuild integration for Microsoft Testing Platform. diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/MSBuildResources.Designer.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/MSBuildResources.Designer.cs new file mode 100644 index 0000000000..04b6104066 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/MSBuildResources.Designer.cs @@ -0,0 +1,189 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Testing.Platform.MSBuild.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class MSBuildResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal MSBuildResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Testing.Platform.MSBuild.Resources.MSBuildResources", typeof(MSBuildResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Actual value: {0}. + /// + internal static string ActualValue { + get { + return ResourceManager.GetString("ActualValue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expected value: {0}. + /// + internal static string ExpectedValue { + get { + return ResourceManager.GetString("ExpectedValue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed. + /// + internal static string Failed { + get { + return ResourceManager.GetString("Failed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed test '{0}', reason '{1}'. + /// + internal static string FailedTestDetail { + get { + return ResourceManager.GetString("FailedTestDetail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Full path tool calculation failed. Runner '{0}'. + /// + internal static string FullPathToolCalculationFailed { + get { + return ResourceManager.GetString("FullPathToolCalculationFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid TargetPath, {0}. + /// + internal static string InvalidTargetPath { + get { + return ResourceManager.GetString("InvalidTargetPath", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extension used to pass parameters from MSBuild node and the hosts. + /// + internal static string MSBuildExtensionsDescription { + get { + return ResourceManager.GetString("MSBuildExtensionsDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Passed. + /// + internal static string Passed { + get { + return ResourceManager.GetString("Passed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Run tests: '{0}' [{1}|{2}]. + /// + internal static string RunTests { + get { + return ResourceManager.GetString("RunTests", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stack Trace:. + /// + internal static string StackTrace { + get { + return ResourceManager.GetString("StackTrace", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5}. + /// + internal static string Summary { + get { + return ResourceManager.GetString("Summary", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tests failed: '{0}' [{1}|{2}]. + /// + internal static string TestFailed { + get { + return ResourceManager.GetString("TestFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tests failed: {0}. + /// + internal static string TestFailedNoDetail { + get { + return ResourceManager.GetString("TestFailedNoDetail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tests succeeded: '{0}' [{1}|{2}]. + /// + internal static string TestsSucceeded { + get { + return ResourceManager.GetString("TestsSucceeded", resourceCulture); + } + } + } +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/MSBuildResources.resx b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/MSBuildResources.resx new file mode 100644 index 0000000000..85c8cfacf9 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/MSBuildResources.resx @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Actual value: {0} + + + Expected value: {0} + + + Failed + + + Failed test '{0}', reason '{1}' + + + Full path tool calculation failed. Runner '{0}' + + + Invalid TargetPath, {0} + + + Extension used to pass parameters from MSBuild node and the hosts + + + Passed + + + Run tests: '{0}' [{1}|{2}] + + + Stack Trace: + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + + + Tests failed: '{0}' [{1}|{2}] + + + Tests failed: {0} + + + Tests succeeded: '{0}' [{1}|{2}] + + diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.cs.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.cs.xlf new file mode 100644 index 0000000000..3daf5a156e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.cs.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + Skutečná hodnota: {0} + + + + Expected value: {0} + Očekávaná hodnota: {0} + + + + Failed + Neúspěšné + + + + Failed test '{0}', reason '{1}' + Neúspěšný test: {0}, důvod: {1} + + + + Full path tool calculation failed. Runner '{0}' + Výpočet úplné cesty nástroje se nezdařil. Spouštěč {0} + + + + Invalid TargetPath, {0} + Neplatná hodnota TargetPath, {0} + + + + Extension used to pass parameters from MSBuild node and the hosts + Rozšíření používané k předání parametrů z uzlu MSBuild a hostitelů + + + + Passed + Úspěšné + + + + Run tests: '{0}' [{1}|{2}] + Spustit testy: {0} [{1}|{2}] + + + + Stack Trace: + Trasování zásobníku: + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}! – neúspěšné: {1}, úspěšné: {2}, přeskočené: {3}, celkem: {4}, doba trvání: {5} + + + + Tests failed: '{0}' [{1}|{2}] + Testy selhaly: {0}[{1}|{2}] + + + + Tests failed: {0} + Testy selhaly: {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + Úspěšné testy: {0} [{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.de.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.de.xlf new file mode 100644 index 0000000000..72faccc947 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.de.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + Tatsächlicher Wert: {0} + + + + Expected value: {0} + Erwarteter Wert: {0} + + + + Failed + Fehler + + + + Failed test '{0}', reason '{1}' + Fehler bei Test "{0}", Ursache "{1}" + + + + Full path tool calculation failed. Runner '{0}' + Fehler bei der Berechnung des vollständigen Pfadtools Runner „{0}“ + + + + Invalid TargetPath, {0} + Ungültiger TargetPath, {0} + + + + Extension used to pass parameters from MSBuild node and the hosts + Erweiterung, die zum Übergeben von Parametern vom MSBuild-Knoten und den Hosts verwendet wird. + + + + Passed + Bestanden + + + + Run tests: '{0}' [{1}|{2}] + Tests ausführen: "{0}" [{1}|{2}] + + + + Stack Trace: + Stapelüberwachung: + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}! - Fehlgeschlagen: {1}, Erfolgreich: {2}, Übersprungen: {3}, Gesamt: {4}, Dauer: {5} + + + + Tests failed: '{0}' [{1}|{2}] + Fehler bei Tests: "{0}" [{1}|{2}] + + + + Tests failed: {0} + Fehler bei Tests: {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + Erfolgreiche Tests: "{0}" [{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.es.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.es.xlf new file mode 100644 index 0000000000..ca9e222100 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.es.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + Valor real: {0} + + + + Expected value: {0} + Valor esperado: {0} + + + + Failed + Error + + + + Failed test '{0}', reason '{1}' + Error en la prueba '{0}', motivo '{1}' + + + + Full path tool calculation failed. Runner '{0}' + El cálculo de la herramienta de ruta de acceso completa es incorrecto. Runner '{0}' + + + + Invalid TargetPath, {0} + TargetPath no válido, {0} + + + + Extension used to pass parameters from MSBuild node and the hosts + Extensión usada para pasar parámetros desde el nodo de MSBuild y los hosts + + + + Passed + Correcta + + + + Run tests: '{0}' [{1}|{2}] + Ejecutar pruebas: '{0}' [{1}|{2}] + + + + Stack Trace: + Seguimiento de la pila: + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}. Error: {1}, Correcto: {2}, Omitido: {3}, Total: {4}, Duración: {5} + + + + Tests failed: '{0}' [{1}|{2}] + Error en las pruebas: '{0}' [{1}|{2}] + + + + Tests failed: {0} + Error en las pruebas: {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + Pruebas correctas: '{0}' [{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.fr.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.fr.xlf new file mode 100644 index 0000000000..f7d32149c8 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.fr.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + Valeur réelle : {0} + + + + Expected value: {0} + Valeur attendue : {0} + + + + Failed + Échec + + + + Failed test '{0}', reason '{1}' + Échec du test « {0} », raison « {1} » + + + + Full path tool calculation failed. Runner '{0}' + Désolé... Nous n’avons pas pu effectuer le calcul d’outil de chemin complet. Exécuteur « {0} » + + + + Invalid TargetPath, {0} + TargetPath non valide, {0} + + + + Extension used to pass parameters from MSBuild node and the hosts + Extension utilisée pour passer des paramètres entre le nœud MSBuild et les hôtes + + + + Passed + Réussite + + + + Run tests: '{0}' [{1}|{2}] + Exécuter des tests : « {0} » [{1}|{2}] + + + + Stack Trace: + Arborescence des appels de procédure : + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}! – Échec : {1}, Passé : {2}, Ignoré : {3}, Total : {4}, Durée : {5} + + + + Tests failed: '{0}' [{1}|{2}] + Échec des tests : « {0} » [{1}|{2}] + + + + Tests failed: {0} + Échec des tests : {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + Tests réussis : « {0} » [{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.it.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.it.xlf new file mode 100644 index 0000000000..b03a93bc12 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.it.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + Valore effettivo: {0} + + + + Expected value: {0} + Valore previsto: {0} + + + + Failed + Errore + + + + Failed test '{0}', reason '{1}' + Test '{0}' non riuscito, motivo '{1}' + + + + Full path tool calculation failed. Runner '{0}' + Calcolo del percorso completo dello strumento non riuscito. Strumento di esecuzione '{0}' + + + + Invalid TargetPath, {0} + TargetPath non valido, {0} + + + + Extension used to pass parameters from MSBuild node and the hosts + Estensione usata per passare parametri dal nodo MSBuild e dagli host + + + + Passed + Superato + + + + Run tests: '{0}' [{1}|{2}] + Esegui test: '{0}' [{1}|{2}] + + + + Stack Trace: + Analisi dello stack: + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}! - Non superati: {1}, Superati: {2}, Ignorati: {3}, Totali: {4}, Durata: {5} + + + + Tests failed: '{0}' [{1}|{2}] + Test non riusciti: '{0}' [{1}|{2}] + + + + Tests failed: {0} + Test non riusciti: {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + Test completati: '{0}' [{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ja.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ja.xlf new file mode 100644 index 0000000000..aa0cfac70e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ja.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + 実際の値: {0} + + + + Expected value: {0} + 想定された値: {0} + + + + Failed + 失敗 + + + + Failed test '{0}', reason '{1}' + 失敗したテスト '{0}'、理由 '{1}' + + + + Full path tool calculation failed. Runner '{0}' + 完全なパス ツールの計算に失敗しました。ランナー '{0}' + + + + Invalid TargetPath, {0} + 無効な TargetPath、{0} + + + + Extension used to pass parameters from MSBuild node and the hosts + MSBuild ノードおよびホストからパラメーターを渡すために使用される拡張機能 + + + + Passed + 成功 + + + + Run tests: '{0}' [{1}|{2}] + テストの実行: '{0}' [{1}|{2}] + + + + Stack Trace: + スタック トレース: + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}! - 不合格: {1}、合格: {2}、スキップ: {3}、合計: {4}、期間: {5} + + + + Tests failed: '{0}' [{1}|{2}] + テスト失敗: '{0}' [{1}|{2}] + + + + Tests failed: {0} + テスト失敗: {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + テスト成功: '{0}' [{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ko.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ko.xlf new file mode 100644 index 0000000000..2eedd90937 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ko.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + 실제 값: {0} + + + + Expected value: {0} + 예상 값: {0} + + + + Failed + 실패 + + + + Failed test '{0}', reason '{1}' + 테스트 '{0}' 실패, 이유 '{1}' + + + + Full path tool calculation failed. Runner '{0}' + 전체 경로 도구를 계산하지 못했습니다. 러너 '{0}' + + + + Invalid TargetPath, {0} + 잘못된 TargetPath, {0} + + + + Extension used to pass parameters from MSBuild node and the hosts + MSBuild 노드 및 호스트에서 매개 변수를 전달하는 데 사용되는 확장 + + + + Passed + 통과 + + + + Run tests: '{0}' [{1}|{2}] + 테스트 실행: '{0}' [{1}|{2}] + + + + Stack Trace: + 스택 추적: + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}! - 실패: {1}, 통과: {2}, 건너뜀: {3}, 합계: {4}, 기간: {5} + + + + Tests failed: '{0}' [{1}|{2}] + 테스트 실패: '{0}' [{1}|{2}] + + + + Tests failed: {0} + 테스트 실패: {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + 테스트 성공: '{0}' [{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pl.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pl.xlf new file mode 100644 index 0000000000..534d74090d --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pl.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + Wartość rzeczywista: {0}. + + + + Expected value: {0} + Oczekiwana wartość: {0}. + + + + Failed + Niepowodzenie + + + + Failed test '{0}', reason '{1}' + Test zakończony niepowodzeniem „{0}”. Przyczyna: „{1}” + + + + Full path tool calculation failed. Runner '{0}' + Obliczanie narzędzia pełnej ścieżki nie powiodło się. Moduł uruchamiający „{0}” + + + + Invalid TargetPath, {0} + Nieprawidłowa ścieżka docelowa, {0} + + + + Extension used to pass parameters from MSBuild node and the hosts + Rozszerzenie używane do przekazywania parametrów z węzła MSBuild i hostów + + + + Passed + Powodzenie + + + + Run tests: '{0}' [{1}|{2}] + Uruchom testy: „{0}” [{1}|{2}] + + + + Stack Trace: + Ślad stosu: + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}! - Niepowodzenie: {1}, Przekazano: {2}, Pominięto: {3}, Łącznie: {4}, Czas trwania: {5} + + + + Tests failed: '{0}' [{1}|{2}] + Niezaliczone testy: „{0}” [{1}|{2}] + + + + Tests failed: {0} + Niezaliczone testy: {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + Testy zakończone powodzeniem: „{0}” [{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pt-BR.xlf new file mode 100644 index 0000000000..c2817a7fc8 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pt-BR.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + Valor real: {0} + + + + Expected value: {0} + Valor esperado: {0} + + + + Failed + Com falha + + + + Failed test '{0}', reason '{1}' + Falha no teste ''{0}'', motivo ''{1}'' + + + + Full path tool calculation failed. Runner '{0}' + Falha no cálculo da ferramenta de caminho completo. Executor “{0}” + + + + Invalid TargetPath, {0} + TargetPath inválido, {0} + + + + Extension used to pass parameters from MSBuild node and the hosts + Extensão usada para passar parâmetros do nó do MSBuild e dos hosts + + + + Passed + Aprovado + + + + Run tests: '{0}' [{1}|{2}] + Executar testes: ''{0}'' [{1}|{2}] + + + + Stack Trace: + Rastreamento de Pilha: + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}! - Falhou: {1}, Aprovado: {2}, Ignorado: {3}, Total: {4}, Duração: {5} + + + + Tests failed: '{0}' [{1}|{2}] + Testes com falha: ''{0}'' [{1}|{2}] + + + + Tests failed: {0} + Testes com falha: {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + Testes bem-sucedidos: ''{0}'' [{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ru.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ru.xlf new file mode 100644 index 0000000000..6454ab5e01 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ru.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + Действительное значение: {0} + + + + Expected value: {0} + Ожидаемое значение: {0} + + + + Failed + Сбой + + + + Failed test '{0}', reason '{1}' + Неудачный тест "{0}" , причина "{1}" + + + + Full path tool calculation failed. Runner '{0}' + Сбой вычисления средства полного пути. Средство выполнения тестов "{0}" + + + + Invalid TargetPath, {0} + Недопустимый TargetPath, {0} + + + + Extension used to pass parameters from MSBuild node and the hosts + Расширение, используемое для передачи параметров с узла MSBuild и из основных элементов + + + + Passed + Пройден + + + + Run tests: '{0}' [{1}|{2}] + Запуск тестов: "{0}" [{1}|{2}] + + + + Stack Trace: + Трассировка стека: + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}! — не пройдено: {1}, пройдено: {2}, пропущено: {3}, всего: {4}, длительность: {5} + + + + Tests failed: '{0}' [{1}|{2}] + Неудачных тестов: "{0}" [{1}|{2}] + + + + Tests failed: {0} + Неудачных тестов: {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + Успешно выполненные тесты: "{0}" [{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.tr.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.tr.xlf new file mode 100644 index 0000000000..ba7880802f --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.tr.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + Gerçek değer: {0} + + + + Expected value: {0} + Beklenen değer: {0} + + + + Failed + Başarısız + + + + Failed test '{0}', reason '{1}' + Başarısız test '{0}', neden '{1}' + + + + Full path tool calculation failed. Runner '{0}' + Tam yol aracı hesaplaması başarısız. Çalıştırıcı '{0}' + + + + Invalid TargetPath, {0} + Geçersiz TargetPath, {0} + + + + Extension used to pass parameters from MSBuild node and the hosts + MSBuild düğümünden ve konaklardan parametreleri geçirmek için kullanılan uzantı + + + + Passed + Başarılı + + + + Run tests: '{0}' [{1}|{2}] + Testleri çalıştırın: '{0}' [{1}|{2}] + + + + Stack Trace: + Yığın İzleme: + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}! - Başarısız: {1}, Başarılı: {2}, Atlandı: {3}, Toplam: {4}, Süre: {5} + + + + Tests failed: '{0}' [{1}|{2}] + Testler başarısız oldu: '{0}' [{1}|{2}] + + + + Tests failed: {0} + Testler başarısız oldu: {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + Başarılı testler: '{0}' [{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hans.xlf new file mode 100644 index 0000000000..c0cb0db8af --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hans.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + 实际值: {0} + + + + Expected value: {0} + 预期的值: {0} + + + + Failed + 失败 + + + + Failed test '{0}', reason '{1}' + 测试“{0}”失败,原因为“{1}” + + + + Full path tool calculation failed. Runner '{0}' + 完整路径工具计算失败。运行器“{0}” + + + + Invalid TargetPath, {0} + TargetPath {0} 无效 + + + + Extension used to pass parameters from MSBuild node and the hosts + 用于从 MSBuild 节点和主机传递参数的扩展 + + + + Passed + 已通过 + + + + Run tests: '{0}' [{1}|{2}] + 运行测试:“{0}”[{1}|{2}] + + + + Stack Trace: + 堆栈跟踪: + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}! - 失败: {1},通过: {2},已跳过: {3},总计: {4},持续时间: {5} + + + + Tests failed: '{0}' [{1}|{2}] + 测试失败:“{0}”[{1}|{2}] + + + + Tests failed: {0} + 测试失败: {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + 成功的测试:“{0}”[{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hant.xlf new file mode 100644 index 0000000000..516842f433 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hant.xlf @@ -0,0 +1,77 @@ + + + + + + Actual value: {0} + 實際值: {0} + + + + Expected value: {0} + 預期的值: {0} + + + + Failed + 失敗 + + + + Failed test '{0}', reason '{1}' + 測試 '{0}' 失敗,原因 '{1}' + + + + Full path tool calculation failed. Runner '{0}' + 完整路徑工具計算失敗。執行器 '{0}' + + + + Invalid TargetPath, {0} + TargetPath 無效,{0} + + + + Extension used to pass parameters from MSBuild node and the hosts + 用於從 MSBuild 節點和主機傳遞參數的延伸模組 + + + + Passed + 成功 + + + + Run tests: '{0}' [{1}|{2}] + 執行測試: '{0}' [{1}|{2}] + + + + Stack Trace: + 堆疊追蹤: + + + + {0}! - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4}, Duration: {5} + {0}! - 失敗: {1},通過: {2},跳過: {3},總計: {4},持續時間: {5} + + + + Tests failed: '{0}' [{1}|{2}] + 測試失敗: '{0}' [{1}|{2}] + + + + Tests failed: {0} + 測試失敗: {0} + + + + Tests succeeded: '{0}' [{1}|{2}] + 測試成功: '{0}' [{1}|{2}] + + + + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/ConfigurationFileTask.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/ConfigurationFileTask.cs new file mode 100644 index 0000000000..d276d00019 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/ConfigurationFileTask.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable CS8618 // Properties below are set by MSBuild. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Platform.MSBuild; + +// Took inspiration from https://github.com/dotnet/sdk/blob/main/src/Tasks/Microsoft.NET.Build.Tasks/GenerateRuntimeConfigurationFiles.cs +public sealed class ConfigurationFileTask : Build.Utilities.Task +{ + private const string ConfigurationFileNameSuffix = "testingplatformconfig.json"; + private readonly IFileSystem _fileSystem; + + internal ConfigurationFileTask(IFileSystem? fileSystem) + { + ArgumentGuard.IsNotNull(fileSystem); + _fileSystem = fileSystem; + } + + public ConfigurationFileTask() + : this(new FileSystem()) + { + } + + [Required] + public ITaskItem TestingPlatformConfigurationFileSource { get; set; } + + [Required] + public ITaskItem MSBuildProjectDirectory { get; set; } + + [Required] + public ITaskItem AssemblyName { get; set; } + + [Required] + public ITaskItem OutputPath { get; set; } + + [Output] + public ITaskItem FinalTestingPlatformConfigurationFile { get; set; } + + public override bool Execute() + { + Log.LogMessage(MessageImportance.Normal, $"Microsoft Testing Platform configuration file: '{TestingPlatformConfigurationFileSource.ItemSpec}'"); + if (!_fileSystem.Exist(TestingPlatformConfigurationFileSource.ItemSpec)) + { + Log.LogMessage(MessageImportance.Normal, $"Microsoft Testing Platform configuration file not found"); + return true; + } + + Log.LogMessage(MessageImportance.Normal, $"MSBuildProjectDirectory: '{MSBuildProjectDirectory.ItemSpec}'"); + Log.LogMessage(MessageImportance.Normal, $"AssemblyName: '{AssemblyName.ItemSpec}'"); + Log.LogMessage(MessageImportance.Normal, $"OutputPath: '{OutputPath.ItemSpec}'"); + + string finalPath = Path.Combine(MSBuildProjectDirectory.ItemSpec, OutputPath.ItemSpec); + Log.LogMessage(MessageImportance.Normal, $"Final path: '{finalPath}'"); + + string finalFileName = Path.Combine(finalPath, $"{AssemblyName.ItemSpec}.{ConfigurationFileNameSuffix}"); + Log.LogMessage(MessageImportance.Normal, $"Final configuration file path : '{finalFileName}'"); + + Log.LogMessage(MessageImportance.Normal, $"Configuration file found: '{TestingPlatformConfigurationFileSource.ItemSpec}'"); + _fileSystem.CopyFile(TestingPlatformConfigurationFileSource.ItemSpec, finalFileName); + FinalTestingPlatformConfigurationFile = new TaskItem(finalFileName); + Log.LogMessage(MessageImportance.Normal, $"Microsoft Testing Platform configuration file written"); + + return true; + } +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/FailedTestHelper.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/FailedTestHelper.cs new file mode 100644 index 0000000000..722f1ae2cd --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/FailedTestHelper.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; +using System.Text; + +using Microsoft.Testing.Platform.MSBuild.TestPlatformExtensions.Serializers; + +namespace Microsoft.Testing.Platform.MSBuild; + +internal static class FailedTestHelper +{ + internal static void FromFailedTest(this FailedTestInfoRequest failedTestInfoRequest, bool outputSupportsMultiline, + string targetPath, + out string errorCode, out string file, out int lineNumber, out string message, out string? lowPriorityMessage) + { + errorCode = failedTestInfoRequest.IsCancelled ? "test cancelled" : "test failed"; + if (StackTraceHelper.TryFindLocationFromStackFrame(failedTestInfoRequest.ErrorStackTrace, out string? filePath, out lineNumber, out string? place)) + { + } + else if (!string.IsNullOrEmpty(failedTestInfoRequest.CodeFilePath)) + { + // If there is no frame with location, but we collect source info, use the source info. + filePath = failedTestInfoRequest.CodeFilePath; + lineNumber = failedTestInfoRequest.LineNumber; + } + else + { + // Use the produced dll. + filePath = targetPath; + } + + // MSBuild Log needs this to be non-null. + file = filePath ?? string.Empty; + + // When multiline is supported, the output will go to screen, and we need to localize it. + // Otherwise it goes to binary log, and that is a "log" and it is better to be in English. + CultureInfo culture = outputSupportsMultiline ? CultureInfo.CurrentCulture : CultureInfo.InvariantCulture; + StringBuilder errorMessage = new(); + errorMessage.Append(failedTestInfoRequest.DisplayName); + if (failedTestInfoRequest.Duration is not null) + { + errorMessage.Append(" ("); + errorMessage.Append(failedTestInfoRequest.Duration); + errorMessage.Append(')'); + } + + errorMessage.Append(": "); + errorMessage.AppendLine(failedTestInfoRequest.ErrorMessage); + + if (!string.IsNullOrEmpty(failedTestInfoRequest.Expected)) + { + errorMessage.AppendLine(string.Format(culture, Resources.MSBuildResources.ExpectedValue, failedTestInfoRequest.Expected)); + errorMessage.AppendLine(string.Format(culture, Resources.MSBuildResources.ActualValue, failedTestInfoRequest.Actual)); + } + + if (!string.IsNullOrEmpty(failedTestInfoRequest.ErrorStackTrace)) + { + errorMessage.AppendLine(string.Format(culture, Resources.MSBuildResources.StackTrace)); + errorMessage.AppendLine(failedTestInfoRequest.ErrorStackTrace); + } + + if (outputSupportsMultiline) + { + // We put all the info to screen via the (high priority) error message, there is no additional info to put into binlog. + lowPriorityMessage = null; + + message = errorMessage.ToString(); + } + else + { + lowPriorityMessage = errorMessage.ToString(); + + string nameAndPlace = place == null + ? $"{failedTestInfoRequest.DisplayName} ({failedTestInfoRequest.Duration})" + : $"{failedTestInfoRequest.DisplayName} ({failedTestInfoRequest.Duration}): {place}"; + string? singleLineError = JoinSingleLineAndShorten(nameAndPlace, failedTestInfoRequest.ErrorMessage); + + message = singleLineError!; + } + } + + private static string? JoinSingleLineAndShorten(string? first, string? second) + => first != null && second != null + ? SingleLineAndShorten(first) + " " + SingleLineAndShorten(second) + : SingleLineAndShorten(first) ?? SingleLineAndShorten(second); + + private static string? SingleLineAndShorten(string? text) +#pragma warning disable IDE0057 // Use range operator + => text == null ? null : (text.Length <= 1000 ? text : text.Substring(0, 1000)).Replace('\r', ' ').Replace('\n', ' '); +#pragma warning restore IDE0057 // Use range operator +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/InvokeTestingPlatformTask.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/InvokeTestingPlatformTask.cs new file mode 100644 index 0000000000..1422779089 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/InvokeTestingPlatformTask.cs @@ -0,0 +1,431 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable CS8618 // Properties below are set by MSBuild. + +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Text; + +using Microsoft.Build.Framework; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Models; +using Microsoft.Testing.Platform.IPC.Serializers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.MSBuild.TestPlatformExtensions; +using Microsoft.Testing.Platform.MSBuild.TestPlatformExtensions.Serializers; + +using Task = System.Threading.Tasks.Task; + +namespace Microsoft.Testing.Platform.MSBuild; + +public class InvokeTestingPlatformTask : Build.Utilities.ToolTask, IDisposable +{ + private const string MonoRunnerName = "mono"; + private const string DotnetRunnerName = "dotnet"; + + private readonly IFileSystem _fileSystem; + private readonly PipeNameDescription _pipeNameDescription; + private readonly CancellationTokenSource _waitForConnections = new(); + private readonly List _connections = new(); + private readonly StringBuilder _output = new(); + private readonly object _initLock = new(); + private readonly Process _currentProcess = Process.GetCurrentProcess(); + private readonly Architecture _currentProcessArchitecture = RuntimeInformation.ProcessArchitecture; + + private Task? _connectionLoopTask; + private ModuleInfoRequest? _moduleInfo; + private string _outputFileName; + private StreamWriter? _outputFileStream; + private string? _toolCommand; + + public InvokeTestingPlatformTask() + : this(new FileSystem()) + { + if (Environment.GetEnvironmentVariable("TESTINGPLATFORM_MSBUILD_LAUNCH_ATTACH_DEBUGGER") == "1") + { + Debugger.Launch(); + } + + _pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + } + + internal InvokeTestingPlatformTask(IFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + [Required] + public ITaskItem TargetPath { get; set; } + + [Required] + public ITaskItem TargetFramework { get; set; } + + [Required] + public ITaskItem TestArchitecture { get; set; } + + [Required] + public ITaskItem TargetFrameworkIdentifier { get; set; } + + [Required] + public ITaskItem TestingPlatformShowTestsFailure { get; set; } + + [Required] + public ITaskItem TestingPlatformCaptureOutput { get; set; } + + [Required] + public ITaskItem ProjectFullPath { get; set; } + + public ITaskItem? DotnetHostPath { get; set; } + + public ITaskItem? TestingPlatformCommandLineArguments { get; set; } + + private bool IsNetCoreApp => TargetFrameworkIdentifier.ItemSpec == ".NETCoreApp"; + + protected override string ToolName + { + get + { + // If target dll ends with .dll we're in the "dotnet" context + if (TargetPath.ItemSpec.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)) + { + Log.LogMessage(MessageImportance.Low, $"Target path is a dll '{TargetPath.ItemSpec}'"); + return DotnetRunnerName + (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty); + } + + // If the target is an exe and we're not on Windows we try with the mono runner. + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && TargetPath.ItemSpec.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase)) + { + Log.LogMessage(MessageImportance.Low, $"Target is an '.exe' on '{RuntimeInformation.OSDescription}' trying with the mono runner."); + return MonoRunnerName; + } + + return Path.GetFileName(TargetPath.ItemSpec); + } + } + + public override string ToolExe { get => base.ToolExe; set => throw new NotSupportedException(); } + + protected override string? GenerateFullPathToTool() + { + // If it's not netcore and we're on Windows we expect the TargetPath to be the executable, otherwise we try with mono. + if (!IsNetCoreApp && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Log.LogMessage(MessageImportance.Low, $"Test application is not a .NETCoreApp, tool path: '{TargetPath.ItemSpec}'"); + return TargetPath.ItemSpec; + } + + string dotnetRunnerName = ToolName; + Log.LogMessage(MessageImportance.Low, $"Tool name: '{dotnetRunnerName}'"); + + // We look for dotnet muxer only if we're not running with mono. + if (dotnetRunnerName != MonoRunnerName) + { + ProcessModule? mainModule = _currentProcess.MainModule; + if (mainModule != null && Path.GetFileName(mainModule.FileName) == dotnetRunnerName) + { + Log.LogMessage(MessageImportance.Low, $"dotnet muxer tool path found using current process: '{mainModule.FileName}' architecture: '{_currentProcessArchitecture}'"); + return mainModule.FileName; + } + + if (DotnetHostPath is not null && File.Exists(DotnetHostPath.ItemSpec)) + { + Log.LogMessage(MessageImportance.Low, $"dotnet muxer tool path found using DOTNET_HOST_PATH environment variable: '{DotnetHostPath.ItemSpec}'"); + return DotnetHostPath.ItemSpec; + } + } + + string values = Environment.GetEnvironmentVariable("PATH")!; + foreach (string? p in values.Split(Path.PathSeparator)) + { + string fullPath = Path.Combine(p, dotnetRunnerName); + if (File.Exists(fullPath)) + { + Log.LogMessage(MessageImportance.Low, $"Runner tool path found using PATH environment variable: '{fullPath}'"); + return fullPath; + } + } + + Log.LogError(Resources.MSBuildResources.FullPathToolCalculationFailed, dotnetRunnerName); + + return null; + } + + private bool IsCurrentProcessArchitectureCompatible() + { +#if NET + Architecture targetArchitecture = Enum.Parse(TestArchitecture.ItemSpec, ignoreCase: true); +#else + var targetArchitecture = (Architecture)Enum.Parse(typeof(Architecture), TestArchitecture.ItemSpec, ignoreCase: true); +#endif + return _currentProcessArchitecture == targetArchitecture; + } + + protected override string GenerateCommandLineCommands() + { + Build.Utilities.CommandLineBuilder builder = new(); + + if (IsNetCoreApp) + { + // Check if we need to use dotnet run with --arch switch + string dotnetRunnerName = ToolName; + if (dotnetRunnerName != MonoRunnerName && Path.GetFileName(_currentProcess.MainModule?.FileName) == dotnetRunnerName) + { + if (!IsCurrentProcessArchitectureCompatible()) + { + Log.LogMessage(MessageImportance.Low, $"Current muxer architecture is not compatible with the TestArchitecture({TestArchitecture}) run with --arch {TestArchitecture.ItemSpec.ToLowerInvariant()}"); + builder.AppendSwitch("run"); + builder.AppendSwitchIfNotNull("--project ", ProjectFullPath.ItemSpec); + builder.AppendSwitchIfNotNull("--framework ", TargetFramework.ItemSpec); + builder.AppendSwitchIfNotNull("--arch ", TestArchitecture.ItemSpec.ToLowerInvariant()); + builder.AppendTextUnquoted($" -- "); + } + else + { + builder.AppendSwitch("exec"); + builder.AppendFileNameIfNotNull(TargetPath.ItemSpec); + } + } + } + else + { + // If the target is an exe and we're not on Windows we try with the mono runner and so we pass the test module to the mono runner. + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && TargetPath.ItemSpec.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase)) + { + builder.AppendFileNameIfNotNull(TargetPath.ItemSpec); + } + } + + builder.AppendSwitchIfNotNull($"--{MSBuildCommandLineProvider.MSBuildNodeOptionKey} ", _pipeNameDescription.Name); + + if (!string.IsNullOrEmpty(TestingPlatformCommandLineArguments?.ItemSpec)) + { + builder.AppendTextUnquoted($" {TestingPlatformCommandLineArguments!.ItemSpec} "); + } + + return builder.ToString(); + } + + protected override bool ValidateParameters() + { + if (!_fileSystem.Exist(TargetPath.ItemSpec)) + { + Log.LogError(Resources.MSBuildResources.InvalidTargetPath, TargetPath.ItemSpec); + return false; + } + + return true; + } + + protected override MessageImportance StandardOutputLoggingImportance + => MessageImportance.Low; + + protected override MessageImportance StandardErrorLoggingImportance + => MessageImportance.Low; + + protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance) + { + if (!bool.Parse(TestingPlatformCaptureOutput.ItemSpec)) + { + Log.LogMessage(MessageImportance.High, singleLine); + } + + // Collect the output to be written to the file. + _output.AppendLine(singleLine); + } + + protected override void ProcessStarted() + => _connectionLoopTask = Task.Run(() => + { + try + { + while (!_waitForConnections.IsCancellationRequested) + { + NamedPipeServer pipeServer = new(_pipeNameDescription, HandleRequestAsync, new SystemEnvironment(), new MSBuildLogger(), new SystemTask(), maxNumberOfServerInstances: 100, CancellationToken.None); + pipeServer.RegisterSerializer(new ModuleInfoRequestSerializer(), typeof(ModuleInfoRequest)); + pipeServer.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + pipeServer.RegisterSerializer(new FailedTestInfoRequestSerializer(), typeof(FailedTestInfoRequest)); + pipeServer.RegisterSerializer(new RunSummaryInfoRequestSerializer(), typeof(RunSummaryInfoRequest)); + pipeServer.WaitConnectionAsync(_waitForConnections.Token).GetAwaiter().GetResult(); + _connections.Add(pipeServer); + Log.LogMessage(MessageImportance.Low, $"Client connected to '{_pipeNameDescription.Name}'"); + } + } + catch (OperationCanceledException) when (_waitForConnections.IsCancellationRequested) + { + // Do nothing we're cancelling + } + }); + + public override bool Execute() + { + bool returnValue = base.Execute(); + if (_toolCommand is not null) + { + StringBuilder sb = new(); + sb.AppendLine(); + sb.AppendLine("=== COMMAND LINE ==="); + sb.AppendLine(_toolCommand); + _output.AppendLine(sb.ToString()); + } + + // Persist the output to the file. + _outputFileStream?.WriteLine(_output); + + _waitForConnections.Cancel(); + Dispose(); + + if (returnValue) + { + Log.LogMessage(MessageImportance.High, Resources.MSBuildResources.TestsSucceeded, TargetPath.ItemSpec.Trim(), TargetFramework.ItemSpec, TestArchitecture.ItemSpec); + } + + return returnValue; + } + + protected override void LogToolCommand(string message) + { + _toolCommand = message; + Log.LogMessage(MessageImportance.Low, $"Tool command: '{message}'"); + Log.LogMessage(MessageImportance.High, Resources.MSBuildResources.RunTests, TargetPath.ItemSpec.Trim(), TargetFramework.ItemSpec, TestArchitecture.ItemSpec); + } + + protected override bool HandleTaskExecutionErrors() + { + // This is an unexpected situation we simply print to the console the output and return false. + if (string.IsNullOrEmpty(_outputFileName) && ExitCode != ExitCodes.InvalidCommandLine) + { + Log.LogError(null, "run failed", null, TargetPath.ItemSpec.Trim(), 0, 0, 0, 0, Resources.MSBuildResources.TestFailedNoDetail, _output); + } + else + { + // If the output file name is null and the exit code is invalid command line we create a default one. + if (_outputFileName is null && ExitCode == ExitCodes.InvalidCommandLine) + { + _outputFileName = Path.Combine(Path.GetDirectoryName(TargetPath.ItemSpec.Trim())!, AggregatedConfiguration.DefaultTestResultFolderName); + _fileSystem.CreateDirectory(_outputFileName); + _outputFileName = Path.Combine(_outputFileName, $"{Path.GetFileNameWithoutExtension(TargetPath.ItemSpec.Trim())}_{TargetFramework.ItemSpec}_{TestArchitecture.ItemSpec}.log"); + Log.LogMessage(MessageImportance.Low, $"Invalid command line exit code and empty output file name, creating default one '{_outputFileName}'"); + _outputFileStream = new StreamWriter(_fileSystem.CreateNew(_outputFileName), Encoding.Unicode) + { + AutoFlush = true, + }; + } + + Log.LogError(null, "run failed", null, TargetPath.ItemSpec.Trim(), 0, 0, 0, 0, Resources.MSBuildResources.TestFailed, _outputFileName, TargetFramework.ItemSpec, TestArchitecture.ItemSpec); + } + + return false; + } + + private Task HandleRequestAsync(IRequest request) + { + if (request is ModuleInfoRequest moduleInfo) + { + if (_moduleInfo is null) + { + lock (_initLock) + { + if (_moduleInfo is null) + { + _moduleInfo = moduleInfo; + _outputFileName = $"{Path.GetFileNameWithoutExtension(TargetPath.ItemSpec.Trim())}_{TargetFramework.ItemSpec}_{TestArchitecture.ItemSpec}.log"; + _outputFileName = Path.Combine(_moduleInfo.TestResultFolder, _outputFileName); + Log.LogMessage(MessageImportance.Low, $"Initializing module info and output file '{_outputFileName}'"); + _outputFileStream = new StreamWriter(_fileSystem.CreateNew(_outputFileName), Encoding.Unicode) + { + AutoFlush = true, + }; + } + } + } + + return Task.FromResult(VoidResponse.CachedInstance); + } + + if (request is FailedTestInfoRequest failedTestInfoRequest) + { + // TestingPlatformShowTestsFailure is not enabled, don't write errors to output. + if (!bool.Parse(TestingPlatformShowTestsFailure.ItemSpec)) + { + return Task.FromResult(VoidResponse.CachedInstance); + } + + failedTestInfoRequest.FromFailedTest(outputSupportsMultiline: MSBuildCompatibilityHelper.SupportsMultiLine(), TargetPath.ItemSpec.Trim(), + out string errorCode, out string file, out int lineNumber, out string message, out string? lowPriorityMessage); + Log.LogError(null, errorCode, null, file, lineNumber, 0, 0, 0, message, null); + if (lowPriorityMessage is not null) + { + Log.LogMessage(MessageImportance.Low, lowPriorityMessage); + } + + return Task.FromResult(VoidResponse.CachedInstance); + } + + if (request is RunSummaryInfoRequest runSummaryInfoRequest) + { + string summary = string.Format( + CultureInfo.CurrentCulture, + Resources.MSBuildResources.Summary, + runSummaryInfoRequest.TotalFailed > 0 || runSummaryInfoRequest.TotalPassed == 0 + ? Resources.MSBuildResources.Failed + : Resources.MSBuildResources.Passed, + runSummaryInfoRequest.TotalFailed, + runSummaryInfoRequest.TotalPassed, + runSummaryInfoRequest.TotalSkipped, + runSummaryInfoRequest.Total, + runSummaryInfoRequest.Duration); + + if (MSBuildCompatibilityHelper.SupportsTerminalLoggerWithExtendedMessages()) + { + var metadata = new Dictionary + { + ["total"] = runSummaryInfoRequest.Total.ToString(CultureInfo.InvariantCulture), + ["passed"] = runSummaryInfoRequest.TotalPassed.ToString(CultureInfo.InvariantCulture), + ["skipped"] = runSummaryInfoRequest.TotalSkipped.ToString(CultureInfo.InvariantCulture), + ["failed"] = runSummaryInfoRequest.TotalFailed.ToString(CultureInfo.InvariantCulture), + ["duration"] = runSummaryInfoRequest.Duration, + }; + + // No need to check the return value, we checked explicitly that the api is supported in the if above. + _ = MSBuildCompatibilityHelper.TryWriteExtendedMessage(BuildEngine, "TLTESTFINISH", summary, metadata); + } + else + { + Log.LogMessage(MessageImportance.High, summary); + } + + return Task.FromResult(VoidResponse.CachedInstance); + } + + throw new NotImplementedException($"Request '{request.GetType()}' not supported."); + } + + private class MSBuildLogger : Logging.ILogger + { + public bool IsEnabled(LogLevel logLevel) => false; + + public void Log(LogLevel logLevel, TState state, Exception? exception, Func formatter) + { + } + + public Task LogAsync(LogLevel logLevel, TState state, Exception? exception, Func formatter) => Task.CompletedTask; + } + + public void Dispose() + { + _outputFileStream?.Dispose(); + _waitForConnections.Cancel(); + _connectionLoopTask?.Wait(); + + foreach (NamedPipeServer serverInstance in _connections) + { + serverInstance.Dispose(); + } + + GC.SuppressFinalize(this); + } +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/MSBuildCompatibilityHelper.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/MSBuildCompatibilityHelper.cs new file mode 100644 index 0000000000..5d96eb1bf9 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/MSBuildCompatibilityHelper.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable CS8618 // Properties below are set by MSBuild. + +using System.Reflection; + +using Microsoft.Build.Framework; + +namespace Microsoft.Testing.Platform.MSBuild; + +/// +/// This class wraps APIs of MSBuild that are newer than the oldest supported .NET SDK. Our task can get loaded into .NET 6 SDK +/// and we need to guard the usage of these newer APIs. +/// +internal static class MSBuildCompatibilityHelper +{ + // This was added in late 17.10.0, the api to call is the old and stable Log api, but if we send multiline messages to that api + // before multiline is supported, we get one message per line, and it looks really messy. + // https://github.com/dotnet/msbuild/pull/9699 + private const string TerminalLoggerMultilineHandlerFeatureName = "TerminalLogger_MultiLineHandler"; + + // Terminal logger testing support that consumes ExtendedMessages was added. + private static readonly Version TerminalLoggerWithExtendedMessagesSupported = new(17, 10, 0); + + // This adds the generic api that allows checking for presence of a feature https://github.com/dotnet/msbuild/pull/9665. + private static readonly Version FeatureChecksAdded = new(17, 10, 0); + + private static Version? s_msBuildVersion; + + private static bool? s_supportsMultiline; + + private static bool? s_supportsTerminalLoggerWithExtendedMessages; + + private static Version GetMsBuildVersion() + { + if (s_msBuildVersion == null) + { + var fileVersionAttribute = (AssemblyFileVersionAttribute?)typeof(ILogger).Assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false).FirstOrDefault(); + s_msBuildVersion = fileVersionAttribute?.Version != null ? new Version(fileVersionAttribute.Version) : new Version(); + } + + return s_msBuildVersion; + } + + public static bool SupportsMultiLine() + { + s_supportsMultiline ??= GetMsBuildVersion() >= FeatureChecksAdded + && MSBuildNewApiWrapper.UnsafeCheckFeature(TerminalLoggerMultilineHandlerFeatureName); + + return s_supportsMultiline.Value; + } + + public static bool SupportsTerminalLoggerWithExtendedMessages() + { + s_supportsTerminalLoggerWithExtendedMessages ??= GetMsBuildVersion() >= TerminalLoggerWithExtendedMessagesSupported; + + return s_supportsTerminalLoggerWithExtendedMessages.Value; + } + + public static bool TryWriteExtendedMessage(IBuildEngine engine, string messageType, string message, Dictionary metadata) + { + if (!SupportsTerminalLoggerWithExtendedMessages()) + { + return false; + } + + MSBuildNewApiWrapper.UnsafeWriteExtendedMessage(engine, messageType, message, metadata); + + return true; + } + + private static class MSBuildNewApiWrapper + { + public static bool UnsafeCheckFeature(string featureName) + { + FeatureStatus featureStatus = Features.CheckFeatureAvailability(featureName); + + return featureStatus is FeatureStatus.Available or FeatureStatus.Preview; + } + + public static void UnsafeWriteExtendedMessage(IBuildEngine engine, string messageType, string message, Dictionary metadata) + { + var extendedMessage = new ExtendedBuildMessageEventArgs(messageType, message, null, null, MessageImportance.High) + { + ExtendedMetadata = metadata, + }; + + engine.LogMessageEvent(extendedMessage); + } + } +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/StackTraceHelper.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/StackTraceHelper.cs new file mode 100644 index 0000000000..04f6ac93b2 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/StackTraceHelper.cs @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace Microsoft.Testing.Platform.MSBuild; + +internal class StackTraceHelper +{ + private static Regex? s_regex; + + internal static bool TryFindLocationFromStackFrame(string? errorStackTrace, [NotNullWhen(true)] out string? file, out int lineNumber, out string? place) + { + file = null; + place = null; + lineNumber = 0; + + if (errorStackTrace == null) + { + return false; + } + + string[] stackFrames = Regex.Split(errorStackTrace, Environment.NewLine); + if (stackFrames.Length == 0) + { + return false; + } + + // Take 20 frames at max, so we don't search 1000 items in a long stack trace. + foreach (string? stackFrame in stackFrames.Take(20)) + { + if (TryGetStackFrameLocation(stackFrame, out lineNumber, out file, out place)) + { + return true; + } + } + + return false; + } + + private static bool TryGetStackFrameLocation(string stackFrame, out int line, [NotNullWhen(true)] out string? file, out string? place) + { + InitializeRegex(); + + // stack frame looks like this ' at Program.
$(String[] args) in S:\t\ConsoleApp81\ConsoleApp81\Program.cs:line 9' + Match match = s_regex.Match(stackFrame); + + line = 0; + file = null; + place = null; + + if (match.Success) + { + // get the exact info from stack frame. + place = match.Groups["code"].Value; + file = match.Groups["file"].Value; + _ = int.TryParse(match.Groups["line"].Value, out line); + } + + return match.Success; + } + + [MemberNotNull(nameof(s_regex))] + private static void InitializeRegex() + { + if (s_regex != null) + { + return; + } + + string atResourceName = "Word_At"; + string inResourceName = "StackTrace_InFileLineNumber"; + + string? atString = null; + string? inString = null; + + // Grab words from localized resource, in case the stack trace is localized. + try + { + // Get these resources: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx + MethodInfo? getResourceStringMethod = typeof(Environment).GetMethod("GetResourceString", BindingFlags.Static | BindingFlags.NonPublic, null, [typeof(string)], null); + if (getResourceStringMethod is not null) + { + // at + atString = (string?)getResourceStringMethod.Invoke(null, [atResourceName]); + + // in {0}:line {1} + inString = (string?)getResourceStringMethod.Invoke(null, [inResourceName]); + } + } + catch + { + // If we fail, populate the defaults below. + } + + atString = atString == null || atString == atResourceName ? "at" : atString; + inString = inString == null || inString == inResourceName ? "in {0}:line {1}" : inString; + + string inPattern = string.Format(CultureInfo.InvariantCulture, inString, "(?.+)", @"(?\d+)"); + + s_regex = new Regex(@$"^ {atString} (?.+) {inPattern}$", RegexOptions.Compiled, matchTimeout: TimeSpan.FromSeconds(1)); + } +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformEntryPointTask.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformEntryPointTask.cs new file mode 100644 index 0000000000..608298f4d7 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformEntryPointTask.cs @@ -0,0 +1,243 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable CS8618 // Properties below are set by MSBuild. + +using System.Globalization; +using System.Text; + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.Testing.Platform.MSBuild; + +public sealed class TestingPlatformEntryPointTask : Build.Utilities.Task +{ + private const string DisplayNameMetadataName = "DisplayName"; + private const string TypeFullNameMetadataName = "TypeFullName"; + private const string WellKnownBuilderHookMicrosoftTestingExtensionsTrx = "2006B3F7-93D2-4D9C-9C69-F41A1F21C9C7"; + private const string CSharpLanguageSymbol = "C#"; + private const string FSharpLanguageSymbol = "F#"; + private const string VBLanguageSymbol = "VB"; + + public TestingPlatformEntryPointTask() + : this(new FileSystem()) + { + } + + internal TestingPlatformEntryPointTask(IFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + [Required] + public ITaskItem TestingPlatformEntryPointSourcePath { get; set; } + + [Required] + public ITaskItem Language { get; set; } + + [Required] + public ITaskItem[] TestingPlatformBuilderHooks { get; set; } + + [Output] + public ITaskItem TestingPlatformEntryPointGeneratedFilePath { get; set; } + + private readonly string _expectedItemSpec = """ +Expected item spec: + + + + + MyBuilderHook + + Contoso.BuilderHook + + +Expected method signature +static Contoso.BuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.TestApplicationBuilder builder, string[] args) +"""; + + private readonly IFileSystem _fileSystem; + + public override bool Execute() + { + Log.LogMessage(MessageImportance.Normal, $"TestingPlatformEntryPointSourcePath: '{TestingPlatformEntryPointSourcePath.ItemSpec}'"); + Log.LogMessage(MessageImportance.Normal, $"Language: '{Language.ItemSpec}'"); + + if (TestingPlatformBuilderHooks.Length == 0) + { + Log.LogError("Zero TestingPlatformBuilderHook items found, add once or disable the testing platform entrypoint generation adding\nfalse\n{0}", _expectedItemSpec); + return false; + } + + if (TestingPlatformBuilderHooks.Length > 0) + { + StringBuilder stringBuilder = new(); + stringBuilder.AppendLine("TestingPlatformExtensionFullTypeNames:"); + + // Distinct by ItemSpec and take the first one. + foreach (ITaskItem item in TestingPlatformBuilderHooks.GroupBy(x => x.ItemSpec).Select(x => x.First())) + { + if (string.IsNullOrEmpty(item.GetMetadata(DisplayNameMetadataName))) + { + Log.LogError("Missing 'DisplayName' metadata for item 'TestingPlatformBuilderHook'\n{0}", _expectedItemSpec); + } + + if (string.IsNullOrEmpty(item.GetMetadata(TypeFullNameMetadataName))) + { + Log.LogError("Missing 'TypeFullName' metadata for item 'TestingPlatformBuilderHook'\n{0}", _expectedItemSpec); + } + + stringBuilder.AppendLine(CultureInfo.InvariantCulture, $" Hook UID: '{item.ItemSpec}' DisplayName: '{item.GetMetadata(DisplayNameMetadataName)}' TypeFullName: '{item.GetMetadata(TypeFullNameMetadataName)}'"); + } + + Log.LogMessage(MessageImportance.Normal, stringBuilder.ToString()); + } + + if (!Log.HasLoggedErrors) + { + ITaskItem[] taskItems = Reorder(TestingPlatformBuilderHooks); + + if (!Language.ItemSpec.Equals(CSharpLanguageSymbol, StringComparison.OrdinalIgnoreCase) && + !Language.ItemSpec.Equals(VBLanguageSymbol, StringComparison.OrdinalIgnoreCase) && + !Language.ItemSpec.Equals(FSharpLanguageSymbol, StringComparison.OrdinalIgnoreCase)) + { + TestingPlatformEntryPointGeneratedFilePath = default!; + Log.LogError($"Language '{Language.ItemSpec}' is not supported."); + } + else + { + GenerateEntryPoint(Language.ItemSpec, taskItems, TestingPlatformEntryPointSourcePath, _fileSystem, Log); + TestingPlatformEntryPointGeneratedFilePath = TestingPlatformEntryPointSourcePath; + } + } + + return !Log.HasLoggedErrors; + } + + private static ITaskItem[] Reorder(ITaskItem[] items) + { + List result = new(items.Length); + int wellKnownBuilderHook_MicrosoftTestingPlatformExtensions_index = -1; + for (int i = 0; i < items.Length; i++) + { + if (items[i].ItemSpec == WellKnownBuilderHookMicrosoftTestingExtensionsTrx) + { + wellKnownBuilderHook_MicrosoftTestingPlatformExtensions_index = i; + continue; + } + + result.Add(items[i]); + } + + if (wellKnownBuilderHook_MicrosoftTestingPlatformExtensions_index != -1) + { + result.Add(items[wellKnownBuilderHook_MicrosoftTestingPlatformExtensions_index]); + } + + return result.ToArray(); + } + + private static void GenerateEntryPoint(string language, ITaskItem[] taskItems, ITaskItem testingPlatformEntryPointSourcePath, IFileSystem fileSystem, TaskLoggingHelper taskLoggingHelper) + { + StringBuilder builder = new(); + + for (int i = 0; i < taskItems.Length; i++) + { + if (i != 0) + { + // Indent + builder.Append(" "); + } + + builder.Append(CultureInfo.InvariantCulture, $"{taskItems[i].GetMetadata(TypeFullNameMetadataName)}.AddExtensions(builder, args){(language == CSharpLanguageSymbol ? ";" : string.Empty)}"); + if (i < taskItems.Length - 1) + { + builder.AppendLine(); + } + } + + string entryPointSource = GetEntryPointSourceCode(language, builder.ToString()); + taskLoggingHelper.LogMessage(MessageImportance.Normal, $"Entrypoint source:\n'{entryPointSource}'"); + fileSystem.WriteAllText(testingPlatformEntryPointSourcePath.ItemSpec, entryPointSource); + } + + private static string GetEntryPointSourceCode(string language, string extensionsFragments) + { + if (language == CSharpLanguageSymbol) + { + return $$""" +//------------------------------------------------------------------------------ +// +// This code was generated by Microsoft.Testing.Platform.MSBuild +// +//------------------------------------------------------------------------------ + +[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] +internal sealed class TestingPlatformEntryPoint +{ + public static async global::System.Threading.Tasks.Task Main(string[] args) + { + global::Microsoft.Testing.Platform.Builder.ITestApplicationBuilder builder = await global::Microsoft.Testing.Platform.Builder.TestApplication.CreateBuilderAsync(args); + {{extensionsFragments}} + using (global::Microsoft.Testing.Platform.Builder.ITestApplication app = await builder.BuildAsync()) + { + return await app.RunAsync(); + } + } +} +"""; + } + else if (language == VBLanguageSymbol) + { + return $$""" +'------------------------------------------------------------------------------ +' +' This code was generated by Microsoft.Testing.Platform.MSBuild +' +'------------------------------------------------------------------------------ + + +Module TestingPlatformEntryPoint + + Function Main(args As Global.System.String()) As Global.System.Int32 + Return MainAsync(args).Result + End Function + + Public Async Function MainAsync(ByVal args() As Global.System.String) As Global.System.Threading.Tasks.Task(Of Integer) + Dim builder = Await Global.Microsoft.Testing.Platform.Builder.TestApplication.CreateBuilderAsync(args) + {{extensionsFragments}} + Using testApplication = Await builder.BuildAsync() + Return Await testApplication.RunAsync() + End Using + End Function + +End Module +"""; + } + else if (language == FSharpLanguageSymbol) + { + return $$""" +//------------------------------------------------------------------------------ +// +// This code was generated by Microsoft.Testing.Platform.MSBuild +// +//------------------------------------------------------------------------------ + +[] +[] +let main args = + task { + let! builder = Microsoft.Testing.Platform.Builder.TestApplication.CreateBuilderAsync args + {{extensionsFragments}} + use! app = builder.BuildAsync() + return! app.RunAsync() + } + |> Async.AwaitTask + |> Async.RunSynchronously +"""; + } + + throw new InvalidOperationException($"Language not supported '{language}'"); + } +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/MSBuildCommandLine.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/MSBuildCommandLine.cs new file mode 100644 index 0000000000..b851811efd --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/MSBuildCommandLine.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Platform.MSBuild.TestPlatformExtensions; + +internal sealed class MSBuildCommandLineProvider : ICommandLineOptionsProvider +{ + public const string MSBuildNodeOptionKey = "internal-msbuild-node"; + + public string Uid => nameof(MSBuildCommandLineProvider); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => nameof(MSBuildCommandLineProvider); + + public string Description => Resources.MSBuildResources.MSBuildExtensionsDescription; + + public IReadOnlyCollection GetCommandLineOptions() + => + [ + new(MSBuildNodeOptionKey, "Used to pass the msbuild node handle", ArgumentArity.ExactlyOne, isHidden: true, isBuiltIn: true) + ]; + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) + => ValidationResult.ValidTask; + + public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) + => ValidationResult.ValidTask; +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/MSBuildConsumer.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/MSBuildConsumer.cs new file mode 100644 index 0000000000..4181ba2cf6 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/MSBuildConsumer.cs @@ -0,0 +1,228 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; +using System.Text; + +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC.Models; +using Microsoft.Testing.Platform.MSBuild.TestPlatformExtensions.Serializers; +using Microsoft.Testing.Platform.Services; +using Microsoft.Testing.Platform.TestHost; + +namespace Microsoft.Testing.Platform.MSBuild.TestPlatformExtensions; + +internal class MSBuildConsumer : IDataConsumer, ITestSessionLifetimeHandler +{ + private readonly IServiceProvider _serviceProvider; + private readonly ICommandLineOptions _commandLineOptions; + private MSBuildTestApplicationLifecycleCallbacks? _msBuildTestApplicationLifecycleCallbacks; + private bool _sessionEnded; + private int _totalTests; + private int _totalFailedTests; + private int _totalPassedTests; + private int _totalSkippedTests; + + public MSBuildConsumer(IServiceProvider serviceProvider, ICommandLineOptions commandLineOptions) + { + _serviceProvider = serviceProvider; + _commandLineOptions = commandLineOptions; + } + + public Type[] DataTypesConsumed { get; } = + [ + typeof(TestNodeUpdateMessage), + typeof(TestRequestExecutionTimeInfo), + ]; + + public string Uid => nameof(MSBuildConsumer); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => nameof(MSBuildConsumer); + + public string Description => Resources.MSBuildResources.MSBuildExtensionsDescription; + + public Task IsEnabledAsync() + => Task.FromResult(_commandLineOptions.IsOptionSet(MSBuildCommandLineProvider.MSBuildNodeOptionKey)); + + public Task OnTestSessionStartingAsync(SessionUid sessionUid, CancellationToken cancellationToken) + { + // We get the pipe from the MSBuildTestApplicationLifecycleCallbacks only if we're enabled. + _msBuildTestApplicationLifecycleCallbacks = _serviceProvider.GetRequiredService(); + return Task.CompletedTask; + } + + public Task OnTestSessionFinishingAsync(SessionUid sessionUid, CancellationToken cancellationToken) + { + _sessionEnded = true; + return Task.CompletedTask; + } + + public async Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + // Avoid processing messages if the session has ended. + if (_sessionEnded) + { + return; + } + + switch (value) + { + case TestNodeUpdateMessage testNodeStateChanged: + TimingProperty? timingProperty = testNodeStateChanged.TestNode.Properties.SingleOrDefault(); + string? duration = timingProperty is null ? null : + ToHumanReadableDuration(timingProperty.GlobalTiming.Duration.TotalMilliseconds); + + TestFileLocationProperty? testFileLocationProperty = testNodeStateChanged.TestNode.Properties.SingleOrDefault(); + + switch (testNodeStateChanged.TestNode.Properties.SingleOrDefault()) + { + case ErrorTestNodeStateProperty errorState: + await HandleFailuresAsync( + testNodeStateChanged.TestNode.DisplayName, + isCancelled: false, + duration: duration, + errorMessage: errorState.Exception?.Message ?? errorState.Explanation, + errorStackTrace: errorState.Exception?.StackTrace, + expected: null, + actual: null, + testFileLocationProperty?.FilePath, + testFileLocationProperty?.LineSpan.Start.Line ?? 0, + cancellationToken); + break; + + case FailedTestNodeStateProperty failedState: + await HandleFailuresAsync( + testNodeStateChanged.TestNode.DisplayName, + isCancelled: false, + duration: duration, + errorMessage: failedState.Exception?.Message ?? failedState.Explanation, + errorStackTrace: failedState.Exception?.StackTrace, + expected: failedState.Exception?.Data["assert.expected"] as string, + actual: failedState.Exception?.Data["assert.actual"] as string, + testFileLocationProperty?.FilePath, + testFileLocationProperty?.LineSpan.Start.Line ?? 0, + cancellationToken); + break; + + case TimeoutTestNodeStateProperty timeoutState: + await HandleFailuresAsync( + testNodeStateChanged.TestNode.DisplayName, + isCancelled: true, + duration: duration, + errorMessage: timeoutState.Exception?.Message ?? timeoutState.Explanation, + errorStackTrace: timeoutState.Exception?.StackTrace, + expected: null, + actual: null, + testFileLocationProperty?.FilePath, + testFileLocationProperty?.LineSpan.Start.Line ?? 0, + cancellationToken); + break; + + case CancelledTestNodeStateProperty cancelledState: + await HandleFailuresAsync( + testNodeStateChanged.TestNode.DisplayName, + isCancelled: true, + duration: duration, + errorMessage: cancelledState.Exception?.Message ?? cancelledState.Explanation, + errorStackTrace: cancelledState.Exception?.StackTrace, + expected: null, + actual: null, + testFileLocationProperty?.FilePath, + testFileLocationProperty?.LineSpan.Start.Line ?? 0, + cancellationToken); + break; + + case PassedTestNodeStateProperty: + _totalTests++; + _totalPassedTests++; + break; + + case SkippedTestNodeStateProperty: + _totalTests++; + _totalSkippedTests++; + break; + } + + break; + + case TestRequestExecutionTimeInfo testRequestExecutionTimeInfo: + await HandleSummaryAsync(testRequestExecutionTimeInfo, cancellationToken); + + break; + } + } + + private async Task HandleFailuresAsync(string testDisplayName, bool isCancelled, string? duration, string? errorMessage, string? errorStackTrace, string? expected, string? actual, string? codeFilePath, int lineNumber, CancellationToken cancellationToken) + { + _totalTests++; + _totalFailedTests++; + ApplicationStateGuard.Ensure(_msBuildTestApplicationLifecycleCallbacks != null); + ApplicationStateGuard.Ensure(_msBuildTestApplicationLifecycleCallbacks.PipeClient != null); + var failedTestInfoRequest = new FailedTestInfoRequest(testDisplayName, isCancelled, duration, errorMessage, errorStackTrace, expected, actual, codeFilePath, lineNumber); + await _msBuildTestApplicationLifecycleCallbacks.PipeClient.RequestReplyAsync(failedTestInfoRequest, cancellationToken); + } + + private async Task HandleSummaryAsync(TestRequestExecutionTimeInfo timeInfo, CancellationToken cancellationToken) + { + string? duration = ToHumanReadableDuration(timeInfo.TimingInfo.Duration.TotalMilliseconds); + + ApplicationStateGuard.Ensure(_msBuildTestApplicationLifecycleCallbacks != null); + ApplicationStateGuard.Ensure(_msBuildTestApplicationLifecycleCallbacks.PipeClient != null); + var runSummaryInfoRequest = new RunSummaryInfoRequest(_totalTests, _totalFailedTests, _totalPassedTests, _totalSkippedTests, duration); + await _msBuildTestApplicationLifecycleCallbacks.PipeClient.RequestReplyAsync(runSummaryInfoRequest, cancellationToken); + } + + private static string? ToHumanReadableDuration(double? durationInMs) + { + if (durationInMs is null or < 0) + { + return null; + } + + var time = TimeSpan.FromMilliseconds(durationInMs.Value); + + StringBuilder stringBuilder = new(); + bool hasParentValue = false; + + if (time.Days > 0) + { + stringBuilder.Append(CultureInfo.InvariantCulture, $"{time.Days}d"); + hasParentValue = true; + } + + if (time.Hours > 0 || hasParentValue) + { + stringBuilder.Append(CultureInfo.InvariantCulture, $"{(hasParentValue ? " " : string.Empty)}{(hasParentValue ? time.Hours.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0') : time.Hours.ToString(CultureInfo.InvariantCulture))}h"); + hasParentValue = true; + } + + if (time.Minutes > 0 || hasParentValue) + { + stringBuilder.Append(CultureInfo.InvariantCulture, $"{(hasParentValue ? " " : string.Empty)}{(hasParentValue ? time.Minutes.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0') : time.Minutes.ToString(CultureInfo.InvariantCulture))}m"); + hasParentValue = true; + } + + if (time.Seconds > 0 || hasParentValue) + { + stringBuilder.Append(CultureInfo.InvariantCulture, $"{(hasParentValue ? " " : string.Empty)}{(hasParentValue ? time.Seconds.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0') : time.Seconds.ToString(CultureInfo.InvariantCulture))}s"); + hasParentValue = true; + } + + if (time.Milliseconds >= 0 || hasParentValue) + { + stringBuilder.Append(CultureInfo.InvariantCulture, $"{(hasParentValue ? " " : string.Empty)}{(hasParentValue ? time.Milliseconds.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0') : time.Milliseconds.ToString(CultureInfo.InvariantCulture))}ms"); + } + + return stringBuilder.ToString(); + } +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/MSBuildExtensions.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/MSBuildExtensions.cs new file mode 100644 index 0000000000..a2421d5dee --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/MSBuildExtensions.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.MSBuild.TestPlatformExtensions; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Platform.MSBuild; + +public static class MSBuildExtensions +{ + public static void AddMSBuild(this ITestApplicationBuilder builder) + { + builder.CommandLine.AddProvider(() => new MSBuildCommandLineProvider()); + builder.TestHost.AddTestApplicationLifecycleCallbacks( + serviceProvider => new MSBuildTestApplicationLifecycleCallbacks( + serviceProvider.GetConfiguration(), + serviceProvider.GetCommandLineOptions(), + serviceProvider.GetTestApplicationCancellationTokenSource())); + + CompositeExtensionFactory compositeExtensionFactory + = new(serviceProvider => new MSBuildConsumer( + serviceProvider, + serviceProvider.GetCommandLineOptions())); + + builder.TestHost.AddDataConsumer(compositeExtensionFactory); + builder.TestHost.AddTestSessionLifetimeHandle(compositeExtensionFactory); + } +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/MSBuildTestApplicationLifecycleCallbacks.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/MSBuildTestApplicationLifecycleCallbacks.cs new file mode 100644 index 0000000000..73621c6f5f --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/MSBuildTestApplicationLifecycleCallbacks.cs @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; + +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Models; +using Microsoft.Testing.Platform.IPC.Serializers; +using Microsoft.Testing.Platform.MSBuild.TestPlatformExtensions; +using Microsoft.Testing.Platform.MSBuild.TestPlatformExtensions.Serializers; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Platform.MSBuild; + +internal class MSBuildTestApplicationLifecycleCallbacks : ITestApplicationLifecycleCallbacks, IDisposable +{ + private readonly IConfiguration _configuration; + private readonly ICommandLineOptions _commandLineOptions; + private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource; + + public MSBuildTestApplicationLifecycleCallbacks( + IConfiguration configuration, + ICommandLineOptions commandLineOptions, + ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource) + { + _configuration = configuration; + _commandLineOptions = commandLineOptions; + _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; + } + + public NamedPipeClient? PipeClient { get; private set; } + + public string Uid => nameof(MSBuildTestApplicationLifecycleCallbacks); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => nameof(MSBuildTestApplicationLifecycleCallbacks); + + public string Description => Resources.MSBuildResources.MSBuildExtensionsDescription; + + public Task IsEnabledAsync() + => Task.FromResult(_commandLineOptions.IsOptionSet(MSBuildCommandLineProvider.MSBuildNodeOptionKey)); + + public async Task BeforeRunAsync(CancellationToken cancellationToken) + { + if (!_commandLineOptions.TryGetOptionArgumentList(MSBuildCommandLineProvider.MSBuildNodeOptionKey, out string[]? msbuildInfo)) + { + throw new InvalidOperationException($"MSBuild pipe name not found in the command line, missing {MSBuildCommandLineProvider.MSBuildNodeOptionKey}"); + } + + if (msbuildInfo is null || msbuildInfo.Length != 1 || string.IsNullOrEmpty(msbuildInfo[0])) + { + throw new InvalidOperationException($"MSBuild pipe name not found in the command line, missing argument for {MSBuildCommandLineProvider.MSBuildNodeOptionKey}"); + } + + PipeClient = new(msbuildInfo[0]); + PipeClient.RegisterSerializer(new ModuleInfoRequestSerializer(), typeof(ModuleInfoRequest)); + PipeClient.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + PipeClient.RegisterSerializer(new FailedTestInfoRequestSerializer(), typeof(FailedTestInfoRequest)); + PipeClient.RegisterSerializer(new RunSummaryInfoRequestSerializer(), typeof(RunSummaryInfoRequest)); + using var cancellationTokenSource = new CancellationTokenSource(TimeoutHelper.DefaultHangTimeSpanTimeout); + using var linkedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, _testApplicationCancellationTokenSource.CancellationToken); + await PipeClient.ConnectAsync(linkedCancellationToken.Token); + await PipeClient.RequestReplyAsync( + new ModuleInfoRequest( + RuntimeInformation.FrameworkDescription, + RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(), + _configuration.GetTestResultDirectory()), + _testApplicationCancellationTokenSource.CancellationToken); + } + + public void Dispose() + => PipeClient?.Dispose(); + + public Task AfterRunAsync(int exitCode, CancellationToken cancellation) => Task.CompletedTask; +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/Serializers/FailedTestInfoRequestSerializer.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/Serializers/FailedTestInfoRequestSerializer.cs new file mode 100644 index 0000000000..9fa49a3859 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/Serializers/FailedTestInfoRequestSerializer.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Platform.MSBuild.TestPlatformExtensions.Serializers; + +internal record FailedTestInfoRequest( + string DisplayName, + bool IsCancelled, + string? Duration, + string? ErrorMessage, + string? ErrorStackTrace, + string? Expected, + string? Actual, + string? CodeFilePath, + int LineNumber) : IRequest; + +internal class FailedTestInfoRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 2; + + public object Deserialize(Stream stream) + => new FailedTestInfoRequest( + ReadString(stream), + ReadInt(stream) == 1, + ReadString(stream), + ReadString(stream), + ReadString(stream), + ReadString(stream), + ReadString(stream), + ReadString(stream), + ReadInt(stream)); + + public void Serialize(object objectToSerialize, Stream stream) + { + var failedTestInfoRequest = (FailedTestInfoRequest)objectToSerialize; + WriteString(stream, failedTestInfoRequest.DisplayName); + WriteInt(stream, failedTestInfoRequest.IsCancelled ? 1 : 0); + WriteString(stream, failedTestInfoRequest.Duration ?? string.Empty); + WriteString(stream, failedTestInfoRequest.ErrorMessage ?? string.Empty); + WriteString(stream, failedTestInfoRequest.ErrorStackTrace ?? string.Empty); + WriteString(stream, failedTestInfoRequest.Expected ?? string.Empty); + WriteString(stream, failedTestInfoRequest.Actual ?? string.Empty); + WriteString(stream, failedTestInfoRequest.CodeFilePath ?? string.Empty); + WriteInt(stream, failedTestInfoRequest.LineNumber); + } +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/Serializers/ModuleInfoRequestSerializer.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/Serializers/ModuleInfoRequestSerializer.cs new file mode 100644 index 0000000000..5539f7d107 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/Serializers/ModuleInfoRequestSerializer.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Platform.MSBuild.TestPlatformExtensions.Serializers; + +internal record ModuleInfoRequest(string FrameworkDescription, string ProcessArchitecture, string TestResultFolder) : IRequest; + +internal class ModuleInfoRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 1; + + public object Deserialize(Stream stream) + => new ModuleInfoRequest(ReadString(stream), ReadString(stream), ReadString(stream)); + + public void Serialize(object objectToSerialize, Stream stream) + { + var moduleInfo = (ModuleInfoRequest)objectToSerialize; + WriteString(stream, moduleInfo.FrameworkDescription); + WriteString(stream, moduleInfo.ProcessArchitecture); + WriteString(stream, moduleInfo.TestResultFolder); + } +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/Serializers/RunSummaryRequestSerializer.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/Serializers/RunSummaryRequestSerializer.cs new file mode 100644 index 0000000000..1703e10b26 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/Serializers/RunSummaryRequestSerializer.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.IPC.Serializers; + +namespace Microsoft.Testing.Platform.MSBuild.TestPlatformExtensions.Serializers; + +internal record RunSummaryInfoRequest( + int Total, + int TotalFailed, + int TotalPassed, + int TotalSkipped, + string? Duration) : IRequest; + +internal class RunSummaryInfoRequestSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 3; + + public object Deserialize(Stream stream) + => new RunSummaryInfoRequest( + ReadInt(stream), + ReadInt(stream), + ReadInt(stream), + ReadInt(stream), + ReadString(stream)); + + public void Serialize(object objectToSerialize, Stream stream) + { + var runSummaryInfoRequest = (RunSummaryInfoRequest)objectToSerialize; + WriteInt(stream, runSummaryInfoRequest.Total); + WriteInt(stream, runSummaryInfoRequest.TotalFailed); + WriteInt(stream, runSummaryInfoRequest.TotalPassed); + WriteInt(stream, runSummaryInfoRequest.TotalSkipped); + WriteString(stream, runSummaryInfoRequest.Duration ?? string.Empty); + } +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/TestingPlatformBuilderHook.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/TestingPlatformBuilderHook.cs new file mode 100644 index 0000000000..53e32087d3 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/TestPlatformExtensions/TestingPlatformBuilderHook.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Builder; + +namespace Microsoft.Testing.Platform.MSBuild; + +public static class TestingPlatformBuilderHook +{ + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _) + => MSBuildExtensions.AddMSBuild(testApplicationBuilder); +} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/build/Microsoft.Testing.Platform.MSBuild.props b/src/Platform/Microsoft.Testing.Platform.MSBuild/build/Microsoft.Testing.Platform.MSBuild.props new file mode 100644 index 0000000000..027ba9b9c6 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/build/Microsoft.Testing.Platform.MSBuild.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/build/Microsoft.Testing.Platform.MSBuild.targets b/src/Platform/Microsoft.Testing.Platform.MSBuild/build/Microsoft.Testing.Platform.MSBuild.targets new file mode 100644 index 0000000000..0ccea6d5d5 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/build/Microsoft.Testing.Platform.MSBuild.targets @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.VSTest.targets b/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.VSTest.targets new file mode 100644 index 0000000000..295a3db7bb --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.VSTest.targets @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.props b/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.props new file mode 100644 index 0000000000..cf8a05c3f6 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.props @@ -0,0 +1,15 @@ + + + + + True + + + + + + Microsoft.Testing.Platform.MSBuild + Microsoft.Testing.Platform.MSBuild.TestingPlatformBuilderHook + + + diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.targets b/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.targets new file mode 100644 index 0000000000..838690e317 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.targets @@ -0,0 +1,260 @@ + + + + + + True + True + + + + + + + + + + + + + + + + + + + $(MSBuildThisFileDirectory)..\_MSBuildTasks\netstandard2.0\ + + + + + + + + <_TestingPlatformConfigurationFileSourceFileName>testingplatformconfig.json + <_TestingPlatformConfigurationFileSourcePath>$([System.IO.Path]::Combine($(MSBuildProjectDirectory),$(_TestingPlatformConfigurationFileSourceFileName))) + <_TestingPlatformConfigurationFileName>$(AssemblyName).testingplatformconfig.json + <_TestingPlatformConfigurationFile>$([System.IO.Path]::Combine($(MSBuildProjectDirectory),$(OutputPath),$(_TestingPlatformConfigurationFileName))) + + + + + + + + + + + $(_TestingPlatformConfigurationFileName) + + + + + + + + + + + + + <_GenerateTestingPlatformEntryPointInputsCachFilePath>$(IntermediateOutputPath)$(MSBuildProjectName).gentestingplatformentrypointinputcache.cache + <_GenerateTestingPlatformEntryPointInputsCachFilePath>$([MSBuild]::NormalizePath($(MSBuildProjectDirectory), $(_GenerateTestingPlatformEntryPointInputsCachFilePath))) + + + + <_GenerateTestingPlatformEntryPointInputsCacheToHash Include="@(TestingPlatformBuilderHook)"/> + + + + + + + + + + + + + + + + + <_TestingPlatformEntryPointSourceNameWithoutExtension>TestPlatformEntryPoint + <_TestingPlatformEntryPointSourceName Condition=" $(Language) == 'C#' " >$(_TestingPlatformEntryPointSourceNameWithoutExtension).cs + <_TestingPlatformEntryPointSourceName Condition=" $(Language) == 'VB' " >$(_TestingPlatformEntryPointSourceNameWithoutExtension).vb + <_TestingPlatformEntryPointSourceName Condition=" $(Language) == 'F#' " >$(_TestingPlatformEntryPointSourceNameWithoutExtension).fs + <_TestingPlatformEntryPointSourcePath>$([System.IO.Path]::Combine($(IntermediateOutputPath),$(_TestingPlatformEntryPointSourceName))) + <_TestingPlatformEntryPointSourcePath>$([MSBuild]::NormalizePath($(MSBuildProjectDirectory), $(_TestingPlatformEntryPointSourcePath))) + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + + False + + <_MSBuildTestInfrastructureAvailable Condition="Exists('$(MSBuildExtensionsPath)\Microsoft.Common.Test.targets')" >True + + + + + + InvokeTestingPlatform + + + + + + + + + + InvokeTestingPlatform + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_TestArchitecture>$(PlatformTarget) + + <_TestArchitecture Condition="('$(_TestArchitecture)' == '' or '$(_TestArchitecture)' == 'AnyCpu') and '$(DefaultAppHostRuntimeIdentifier)' != ''">$([System.Text.RegularExpressions.Regex]::Replace($(DefaultAppHostRuntimeIdentifier), '.*-', '')) + + <_TestArchitecture Condition="('$(_TestArchitecture)' == '' or '$(_TestArchitecture)' == 'AnyCpu') and '$(NETCoreSdkRuntimeIdentifier)' != ''">$([System.Text.RegularExpressions.Regex]::Replace($(DefaultAppHostRuntimeIdentifier), '.*-', '')) + + <_TestArchitecture Condition="'$(_TestArchitecture)' == '' or '$(_TestArchitecture)' == 'AnyCpu'">x64 + False + True + + + + + + + + + diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/buildTransitive/Microsoft.Testing.Platform.MSBuild.props b/src/Platform/Microsoft.Testing.Platform.MSBuild/buildTransitive/Microsoft.Testing.Platform.MSBuild.props new file mode 100644 index 0000000000..027ba9b9c6 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/buildTransitive/Microsoft.Testing.Platform.MSBuild.props @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/buildTransitive/Microsoft.Testing.Platform.MSBuild.targets b/src/Platform/Microsoft.Testing.Platform.MSBuild/buildTransitive/Microsoft.Testing.Platform.MSBuild.targets new file mode 100644 index 0000000000..0ccea6d5d5 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/buildTransitive/Microsoft.Testing.Platform.MSBuild.targets @@ -0,0 +1,3 @@ + + + diff --git a/src/Platform/Microsoft.Testing.Platform/.editorconfig b/src/Platform/Microsoft.Testing.Platform/.editorconfig new file mode 100644 index 0000000000..5c8e5dad5d --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/.editorconfig @@ -0,0 +1,3 @@ +[*.{cs,vb}] +# TPEXP: Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +dotnet_diagnostic.TPEXP.severity = none diff --git a/src/Platform/Microsoft.Testing.Platform/Builder/TestApplication.cs b/src/Platform/Microsoft.Testing.Platform/Builder/TestApplication.cs index 907cfcd7db..6ca4b20638 100644 --- a/src/Platform/Microsoft.Testing.Platform/Builder/TestApplication.cs +++ b/src/Platform/Microsoft.Testing.Platform/Builder/TestApplication.cs @@ -114,7 +114,7 @@ public static async Task CreateBuilderAsync(string[] ar } // All checks are fine, create the TestApplication. - return new TestApplicationBuilder(args, loggingState, createBuilderStart, testApplicationOptions, s_unhandledExceptionHandler); + return new TestApplicationBuilder(loggingState, createBuilderStart, testApplicationOptions, s_unhandledExceptionHandler); } private static async Task LogInformationAsync( @@ -142,7 +142,8 @@ private static async Task LogInformationAsync( await logger.LogInformationAsync("Logging mode: " + (syncWrite ? "synchronous" : "asynchronous")); await logger.LogInformationAsync($"Logging level: {loggerLevel}"); await logger.LogInformationAsync($"CreateBuilderAsync entry time: {createBuilderEntryTime}"); - await logger.LogInformationAsync($"PID: {processHandler.GetCurrentProcess().Id}"); + using IProcess currentProcess = processHandler.GetCurrentProcess(); + await logger.LogInformationAsync($"PID: {currentProcess.Id}"); #if NETCOREAPP string runtimeInformation = $"{RuntimeInformation.RuntimeIdentifier} - {RuntimeInformation.FrameworkDescription}"; @@ -156,7 +157,7 @@ private static async Task LogInformationAsync( if (RuntimeFeature.IsDynamicCodeSupported) { #pragma warning disable IL3000 // Avoid accessing Assembly file path when publishing as a single file - string? runtimeLocation = typeof(object).Assembly?.Location; + string? runtimeLocation = typeof(object).Assembly.Location; #pragma warning restore IL3000 // Avoid accessing Assembly file path when publishing as a single file if (runtimeLocation is not null) { @@ -212,23 +213,20 @@ private static async Task LogInformationAsync( if (testHostControllerInfo.HasTestHostController) { - string? processCorrelationId; int? testHostControllerPID = testHostControllerInfo.GetTestHostControllerPID(); - if ((processCorrelationId = environment.GetEnvironmentVariable($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_CORRELATIONID}_{testHostControllerPID}")) is not null) - { - await logger.LogDebugAsync($"{$"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_CORRELATIONID}_{testHostControllerPID}"} '{processCorrelationId}'"); - } - string? parentPid; - if ((parentPid = environment.GetEnvironmentVariable($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_PARENTPID}_{testHostControllerPID}")) is not null) - { - await logger.LogDebugAsync($"{$"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_PARENTPID}_{testHostControllerPID}"} '{parentPid}'"); - } + await LogVariableAsync(EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_CORRELATIONID); + await LogVariableAsync(EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_PARENTPID); + await LogVariableAsync(EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_TESTHOSTPROCESSSTARTTIME); - string? testHostProcessStartTime; - if ((testHostProcessStartTime = environment.GetEnvironmentVariable($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_TESTHOSTPROCESSSTARTTIME}_{testHostControllerPID}")) is not null) + async Task LogVariableAsync(string key) { - await logger.LogDebugAsync($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_TESTHOSTPROCESSSTARTTIME}_{testHostControllerPID} '{testHostProcessStartTime}'"); + string? value; + key = $"{key}_{testHostControllerPID}"; + if ((value = environment.GetEnvironmentVariable(key)) is not null) + { + await logger.LogDebugAsync($"{key} '{value}'"); + } } } @@ -299,11 +297,7 @@ private static ApplicationLoggingState CreateFileLoggerIfDiagnosticIsEnabled( if (result.TryGetOptionArgumentList(PlatformCommandLineProvider.DiagnosticVerbosityOptionKey, out string[]? verbosity)) { -#if NET - logLevel = Enum.Parse(verbosity[0], true); -#else - logLevel = (LogLevel)Enum.Parse(typeof(LogLevel), verbosity[0], true); -#endif + logLevel = EnumPolyfill.Parse(verbosity[0], true); } // Override the log level if the environment variable is set diff --git a/src/Platform/Microsoft.Testing.Platform/Builder/TestApplicationBuilder.cs b/src/Platform/Microsoft.Testing.Platform/Builder/TestApplicationBuilder.cs index 13ab3e216a..91ad6a032d 100644 --- a/src/Platform/Microsoft.Testing.Platform/Builder/TestApplicationBuilder.cs +++ b/src/Platform/Microsoft.Testing.Platform/Builder/TestApplicationBuilder.cs @@ -26,25 +26,22 @@ namespace Microsoft.Testing.Platform.Builder; ///
internal sealed class TestApplicationBuilder : ITestApplicationBuilder { - private readonly string[] _args; private readonly DateTimeOffset _createBuilderStart; private readonly ApplicationLoggingState _loggingState; private readonly TestApplicationOptions _testApplicationOptions; private readonly IUnhandledExceptionsHandler _unhandledExceptionsHandler; private readonly TestHostBuilder _testHostBuilder; private ITestHost? _testHost; - private Func? _testFrameworkAdapterFactory; + private Func? _testFrameworkFactory; private Func? _testFrameworkCapabilitiesFactory; internal TestApplicationBuilder( - string[] args, ApplicationLoggingState loggingState, DateTimeOffset createBuilderStart, TestApplicationOptions testApplicationOptions, IUnhandledExceptionsHandler unhandledExceptionsHandler) { _testHostBuilder = new TestHostBuilder(new SystemFileSystem(), new SystemRuntimeFeature(), new SystemEnvironment(), new SystemProcessHandler(), new CurrentTestApplicationModuleInfo(new SystemEnvironment(), new SystemProcessHandler())); - _args = args; _createBuilderStart = createBuilderStart; _loggingState = loggingState; _testApplicationOptions = testApplicationOptions; @@ -78,12 +75,12 @@ public ITestApplicationBuilder RegisterTestFramework( ArgumentGuard.IsNotNull(adapterFactory); ArgumentGuard.IsNotNull(capabilitiesFactory); - if (_testFrameworkAdapterFactory is not null) + if (_testFrameworkFactory is not null) { throw new InvalidOperationException(PlatformResources.TestApplicationBuilderFrameworkAdapterFactoryAlreadyRegisteredErrorMessage); } - _testFrameworkAdapterFactory = adapterFactory; + _testFrameworkFactory = adapterFactory; if (_testFrameworkCapabilitiesFactory is not null) { @@ -93,14 +90,14 @@ public ITestApplicationBuilder RegisterTestFramework( _testFrameworkCapabilitiesFactory = capabilitiesFactory; _testHostBuilder.TestFramework - = new TestFrameworkManager(_testFrameworkAdapterFactory, _testFrameworkCapabilitiesFactory); + = new TestFrameworkManager(_testFrameworkFactory, _testFrameworkCapabilitiesFactory); return this; } public async Task BuildAsync() { - if (_testFrameworkAdapterFactory is null) + if (_testFrameworkFactory is null) { throw new InvalidOperationException(PlatformResources.TestApplicationBuilderTestFrameworkNotRegistered); } @@ -110,12 +107,7 @@ public async Task BuildAsync() throw new InvalidOperationException(PlatformResources.TestApplicationBuilderApplicationAlreadyRegistered); } - _testHost = await _testHostBuilder.BuildAsync( - _args, - _loggingState, - _testApplicationOptions, - _unhandledExceptionsHandler, - _createBuilderStart); + _testHost = await _testHostBuilder.BuildAsync(_loggingState, _testApplicationOptions, _unhandledExceptionsHandler, _createBuilderStart); return new TestApplication(_testHost); } diff --git a/src/Platform/Microsoft.Testing.Platform/Capabilities/ICapability.cs b/src/Platform/Microsoft.Testing.Platform/Capabilities/ICapability.cs index 2465b0dded..4f2060f317 100644 --- a/src/Platform/Microsoft.Testing.Platform/Capabilities/ICapability.cs +++ b/src/Platform/Microsoft.Testing.Platform/Capabilities/ICapability.cs @@ -6,6 +6,4 @@ namespace Microsoft.Testing.Platform.Capabilities; /// /// Represents a capability. /// -public interface ICapability -{ -} +public interface ICapability; diff --git a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/IBannerMessageOwnerCapability.cs b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/IBannerMessageOwnerCapability.cs new file mode 100644 index 0000000000..13f0fb4571 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/IBannerMessageOwnerCapability.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.Testing.Platform.Capabilities.TestFramework; + +/// +/// An optional test framework capability that allows the test framework to provide the banner message to the platform. +/// If the message is null or if the capability is not present, the platform will use its default banner message. +/// +/// This capability implementation allows to abstract away the various conditions that the test framework may need to consider +/// to decide whether or not the banner message should be displayed. +/// +[Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] +public interface IBannerMessageOwnerCapability : ITestFrameworkCapability +{ + /// + /// Process the banner message and return the message to be displayed. + /// + /// + /// The banner message to be displayed or null to use the default banner message. + /// + Task GetBannerMessageAsync(); +} diff --git a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFrameworkAdapter/INamedFeatureCapability.cs b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/INamedFeatureCapability.cs similarity index 100% rename from src/Platform/Microsoft.Testing.Platform/Capabilities/TestFrameworkAdapter/INamedFeatureCapability.cs rename to src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/INamedFeatureCapability.cs diff --git a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFrameworkAdapter/ITestFrameworkCapabilities.cs b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/ITestFrameworkCapabilities.cs similarity index 97% rename from src/Platform/Microsoft.Testing.Platform/Capabilities/TestFrameworkAdapter/ITestFrameworkCapabilities.cs rename to src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/ITestFrameworkCapabilities.cs index 03a4b35d4f..ead89dcf6e 100644 --- a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFrameworkAdapter/ITestFrameworkCapabilities.cs +++ b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/ITestFrameworkCapabilities.cs @@ -6,9 +6,7 @@ namespace Microsoft.Testing.Platform.Capabilities.TestFramework; /// /// Represents the capabilities of a test framework. /// -public interface ITestFrameworkCapabilities : ICapabilities -{ -} +public interface ITestFrameworkCapabilities : ICapabilities; /// /// Represents the capabilities of a test framework. diff --git a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFrameworkAdapter/ITestFrameworkCapability.cs b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/ITestFrameworkCapability.cs similarity index 83% rename from src/Platform/Microsoft.Testing.Platform/Capabilities/TestFrameworkAdapter/ITestFrameworkCapability.cs rename to src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/ITestFrameworkCapability.cs index 578c35e0fe..5ce8fc74e4 100644 --- a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFrameworkAdapter/ITestFrameworkCapability.cs +++ b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/ITestFrameworkCapability.cs @@ -6,6 +6,4 @@ namespace Microsoft.Testing.Platform.Capabilities.TestFramework; /// /// Represents a capability for a test framework. /// -public interface ITestFrameworkCapability : ICapability -{ -} +public interface ITestFrameworkCapability : ICapability; diff --git a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFrameworkAdapter/ITestNodesTreeFilterTestFrameworkCapability.cs b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/ITestNodesTreeFilterTestFrameworkCapability.cs similarity index 100% rename from src/Platform/Microsoft.Testing.Platform/Capabilities/TestFrameworkAdapter/ITestNodesTreeFilterTestFrameworkCapability.cs rename to src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/ITestNodesTreeFilterTestFrameworkCapability.cs diff --git a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFrameworkAdapter/TestFrameworkCapabilitiesExtensions.cs b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/TestFrameworkCapabilitiesExtensions.cs similarity index 100% rename from src/Platform/Microsoft.Testing.Platform/Capabilities/TestFrameworkAdapter/TestFrameworkCapabilitiesExtensions.cs rename to src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/TestFrameworkCapabilitiesExtensions.cs diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineHandler.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineHandler.cs index facc3ac6b0..9ed0d97426 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineHandler.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineHandler.cs @@ -5,9 +5,7 @@ using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; -using System.Text; -using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.CommandLine; using Microsoft.Testing.Platform.Extensions.OutputDevice; using Microsoft.Testing.Platform.Helpers; @@ -18,31 +16,39 @@ namespace Microsoft.Testing.Platform.CommandLine; -internal sealed class CommandLineHandler(string[] args, CommandLineParseResult parseResult, ICommandLineOptionsProvider[] extensionsCommandLineOptionsProviders, - ICommandLineOptionsProvider[] systemCommandLineOptionsProviders, ITestApplicationModuleInfo testApplicationModuleInfo, IRuntimeFeature runtimeFeature, - IPlatformOutputDevice platformOutputDevice, IEnvironment environment, IProcessHandler process) : ICommandLineHandler, ICommandLineOptions, IOutputDeviceDataProducer +internal sealed class CommandLineHandler : ICommandLineHandler, ICommandLineOptions, IOutputDeviceDataProducer { private static readonly TextOutputDeviceData EmptyText = new(string.Empty); - private readonly ICommandLineOptionsProvider[] _systemCommandLineOptionsProviders = systemCommandLineOptionsProviders; - private readonly ITestApplicationModuleInfo _testApplicationModuleInfo = testApplicationModuleInfo; - private readonly IRuntimeFeature _runtimeFeature = runtimeFeature; - private readonly IPlatformOutputDevice _platformOutputDevice = platformOutputDevice; + private readonly ITestApplicationModuleInfo _testApplicationModuleInfo; + private readonly IPlatformOutputDevice _platformOutputDevice; + private readonly IRuntimeFeature _runtimeFeature; #if !NETCOREAPP [SuppressMessage("CodeQuality", "IDE0052:RemoveVariable unread private members", Justification = "Used in netcoreapp")] #endif - private readonly IEnvironment _environment = environment; + private readonly IEnvironment _environment; #if NETCOREAPP [SuppressMessage("CodeQuality", "IDE0052:RemoveVariable unread private members", Justification = "Used in netstandard")] #endif - private readonly IProcessHandler _process = process; + private readonly IProcessHandler _process; - private readonly CommandLineParseResult _parseResult = parseResult; - - public string[] Arguments { get; } = args; + public CommandLineHandler(CommandLineParseResult parseResult, IReadOnlyCollection extensionsCommandLineOptionsProviders, + IReadOnlyCollection systemCommandLineOptionsProviders, ITestApplicationModuleInfo testApplicationModuleInfo, + IRuntimeFeature runtimeFeature, IPlatformOutputDevice platformOutputDevice, IEnvironment environment, IProcessHandler process) + { + ParseResult = parseResult; + ExtensionsCommandLineOptionsProviders = extensionsCommandLineOptionsProviders; + SystemCommandLineOptionsProviders = systemCommandLineOptionsProviders; + CommandLineOptionsProviders = systemCommandLineOptionsProviders.Union(extensionsCommandLineOptionsProviders); + _testApplicationModuleInfo = testApplicationModuleInfo; + _runtimeFeature = runtimeFeature; + _platformOutputDevice = platformOutputDevice; + _environment = environment; + _process = process; + } - public ICommandLineOptionsProvider[] ExtensionsCommandLineOptionsProviders { get; } = extensionsCommandLineOptionsProviders; + public IEnumerable CommandLineOptionsProviders { get; } public string Uid => nameof(CommandLineHandler); @@ -52,366 +58,11 @@ internal sealed class CommandLineHandler(string[] args, CommandLineParseResult p public string Description => string.Empty; - public async Task<(bool IsValid, string? ValidationError)> TryParseAndValidateAsync() - { - if (_parseResult.HasError) - { - StringBuilder stringBuilder = new(); - stringBuilder.AppendLine(PlatformResources.InvalidCommandLineArguments); - foreach (string error in _parseResult.Errors) - { - stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"\t- {error}"); - } - - return (false, stringBuilder.ToString()); - } - - if (ExtensionOptionsContainReservedPrefix(out string? reservedPrefixError)) - { - return (false, reservedPrefixError); - } - - if (ExtensionOptionsContainReservedOptions(out string? reservedOptionError)) - { - return (false, reservedOptionError); - } - - if (ExtensionOptionAreDuplicated(out string? duplicationError)) - { - return (false, duplicationError); - } - - if (UnknownOptions(out string? unknownOptionsError)) - { - return (false, unknownOptionsError); - } - - if (ExtensionArgumentArityAreInvalid(out string? arityErrors)) - { - return (false, arityErrors); - } - - ValidationResult optionsResult = await ValidateOptionsArgumentsAsync(); - if (!optionsResult.IsValid) - { - return (false, optionsResult.ErrorMessage); - } - - ValidationResult configurationResult = await ValidateConfigurationAsync(); -#pragma warning disable IDE0046 // Convert to conditional expression - make the code less readable - if (!configurationResult.IsValid) - { - return (false, configurationResult.ErrorMessage); - } -#pragma warning restore IDE0046 // Convert to conditional expression - - return (true, null); - } - - private bool ExtensionOptionsContainReservedPrefix([NotNullWhen(true)] out string? error) - { - StringBuilder? stringBuilder = null; - foreach (ICommandLineOptionsProvider commandLineOptionsProvider in ExtensionsCommandLineOptionsProviders) - { - foreach (CommandLineOption option in commandLineOptionsProvider.GetCommandLineOptions()) - { - if (option.IsBuiltIn) - { - continue; - } - - string trimmedOption = option.Name.Trim(CommandLineParseResult.OptionPrefix); - if (trimmedOption.StartsWith("internal", StringComparison.OrdinalIgnoreCase) - || option.Name.StartsWith("-internal", StringComparison.OrdinalIgnoreCase)) - { - stringBuilder ??= new(); - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionIsUsingReservedPrefix, trimmedOption, commandLineOptionsProvider.DisplayName, commandLineOptionsProvider.Uid)); - } - } - } - - error = stringBuilder?.ToString(); - return stringBuilder?.Length > 0; - } - - private async Task ValidateConfigurationAsync() - { - StringBuilder? stringBuilder = null; - foreach (ICommandLineOptionsProvider commandLineOptionsProvider in _systemCommandLineOptionsProviders.Union(ExtensionsCommandLineOptionsProviders)) - { - ValidationResult result = await commandLineOptionsProvider.ValidateCommandLineOptionsAsync(this); - if (!result.IsValid) - { - stringBuilder ??= new(); - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineInvalidConfiguration, commandLineOptionsProvider.DisplayName, commandLineOptionsProvider.Uid, result.ErrorMessage)); - stringBuilder.AppendLine(); - } - } - - return stringBuilder?.Length > 0 - ? ValidationResult.Invalid(stringBuilder.ToString()) - : ValidationResult.Valid(); - } - - private async Task ValidateOptionsArgumentsAsync() - { - ApplicationStateGuard.Ensure(_parseResult is not null); - - StringBuilder? stringBuilder = null; - foreach (OptionRecord optionRecord in _parseResult.Options) - { - ICommandLineOptionsProvider extension = GetAllCommandLineOptionsProviderByOptionName(optionRecord.Option).Single(); - ValidationResult result = await extension.ValidateOptionArgumentsAsync(extension.GetCommandLineOptions().Single(x => x.Name == optionRecord.Option), optionRecord.Arguments); - if (!result.IsValid) - { - stringBuilder ??= new(); - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineInvalidArgumentsForOption, optionRecord.Option, result.ErrorMessage)); - } - } - - return stringBuilder?.Length > 0 - ? ValidationResult.Invalid(stringBuilder.ToString()) - : ValidationResult.Valid(); - } - - private bool UnknownOptions([NotNullWhen(true)] out string? error) - { - error = null; - - ApplicationStateGuard.Ensure(_parseResult is not null); - - StringBuilder? stringBuilder = null; - foreach (OptionRecord optionRecord in _parseResult.Options) - { - if (!GetAllCommandLineOptionsProviderByOptionName(optionRecord.Option).Any()) - { - stringBuilder ??= new(); - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineUnknownOption, optionRecord.Option)); - } - } - - if (stringBuilder?.Length > 0) - { - error = stringBuilder.ToString(); - return true; - } - - return false; - } - - private bool ExtensionArgumentArityAreInvalid([NotNullWhen(true)] out string? error) - { - error = null; - - ApplicationStateGuard.Ensure(_parseResult is not null); - - StringBuilder stringBuilder = new(); - foreach (IGrouping groupedOptions in _parseResult.Options.GroupBy(x => x.Option)) - { - // getting the arguments count for an option. - int arity = 0; - foreach (OptionRecord optionEntry in groupedOptions) - { - arity += optionEntry.Arguments.Length; - } - - string optionName = groupedOptions.Key; - ICommandLineOptionsProvider extension = GetAllCommandLineOptionsProviderByOptionName(optionName).Single(); - CommandLineOption commandLineOption = extension.GetCommandLineOptions().Single(x => x.Name == optionName); - - if (arity > commandLineOption.Arity.Max && commandLineOption.Arity.Max == 0) - { - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionExpectsNoArguments, optionName, extension.DisplayName, extension.Uid)); - } - else if (arity < commandLineOption.Arity.Min) - { - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionExpectsAtLeastArguments, optionName, extension.DisplayName, extension.Uid, commandLineOption.Arity.Min)); - } - else if (arity > commandLineOption.Arity.Max) - { - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionExpectsAtMostArguments, optionName, extension.DisplayName, extension.Uid, commandLineOption.Arity.Max)); - } - } - - if (stringBuilder.Length > 0) - { - error = stringBuilder.ToString(); - return true; - } - - return false; - } - - private bool ExtensionOptionAreDuplicated([NotNullWhen(true)] out string? error) - { - error = null; - IEnumerable duplications = ExtensionsCommandLineOptionsProviders.SelectMany(x => x.GetCommandLineOptions()) - .Select(x => x.Name) - .GroupBy(x => x) - .Where(x => x.Count() > 1) - .Select(x => x.Key); - - StringBuilder? stringBuilder = null; - foreach (string duplicatedOption in duplications) - { - IEnumerable commandLineOptionProviders = GetExtensionCommandLineOptionsProviderByOptionName(duplicatedOption); - stringBuilder ??= new(); - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionIsDeclaredByMultipleProviders, duplicatedOption, string.Join("', '", commandLineOptionProviders.Select(x => x.DisplayName)))); - } - - if (stringBuilder?.Length > 0) - { - stringBuilder.AppendLine(PlatformResources.CommandLineOptionIsDeclaredByMultipleProvidersWorkaround); - error = stringBuilder.ToString(); - return true; - } - - return false; - } - - private bool ExtensionOptionsContainReservedOptions([NotNullWhen(true)] out string? error) - { - error = null; - - IEnumerable allExtensionOptions = ExtensionsCommandLineOptionsProviders.SelectMany(x => x.GetCommandLineOptions()).Select(x => x.Name).Distinct(); - IEnumerable allSystemOptions = _systemCommandLineOptionsProviders.SelectMany(x => x.GetCommandLineOptions()).Select(x => x.Name).Distinct(); - - IEnumerable invalidReservedOptions = allSystemOptions.Intersect(allExtensionOptions); - StringBuilder? stringBuilder = null; - if (invalidReservedOptions.Any()) - { - stringBuilder = new(); - foreach (string reservedOption in invalidReservedOptions) - { - IEnumerable commandLineOptionProviders = GetExtensionCommandLineOptionsProviderByOptionName(reservedOption); - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionIsReserved, reservedOption, string.Join("', '", commandLineOptionProviders.Select(x => x.DisplayName)))); - } - - error = stringBuilder.ToString(); - return true; - } - - return false; - } - - private IEnumerable GetExtensionCommandLineOptionsProviderByOptionName(string optionName) - { - foreach (ICommandLineOptionsProvider commandLineOptionsProvider in ExtensionsCommandLineOptionsProviders) - { - if (commandLineOptionsProvider.GetCommandLineOptions().Any(option => option.Name == optionName)) - { - yield return commandLineOptionsProvider; - } - } - } - - private IEnumerable GetAllCommandLineOptionsProviderByOptionName(string optionName) - { - foreach (ICommandLineOptionsProvider commandLineOptionsProvider in _systemCommandLineOptionsProviders.Union(ExtensionsCommandLineOptionsProviders)) - { - if (commandLineOptionsProvider.GetCommandLineOptions().Any(option => option.Name == optionName)) - { - yield return commandLineOptionsProvider; - } - } - } - - public bool IsHelpInvoked() => IsOptionSet(PlatformCommandLineProvider.HelpOptionKey); - - public bool IsInfoInvoked() => IsOptionSet(PlatformCommandLineProvider.InfoOptionKey); - -#pragma warning disable IDE0060 // Remove unused parameter, temporary we don't use it. - public async Task PrintHelpAsync(ITool[]? availableTools = null) -#pragma warning restore IDE0060 // Remove unused parameter - { - string applicationName = GetApplicationName(_testApplicationModuleInfo); - await PrintApplicationUsageAsync(applicationName); - - // Temporary disabled, we don't remove the code because could be useful in future. - // PrintApplicationToolUsage(availableTools, applicationName); - await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Empty)); - - // Local functions - static string GetApplicationName(ITestApplicationModuleInfo testApplicationModuleInfo) - => testApplicationModuleInfo.IsAppHostOrSingleFileOrNativeAot - ? Path.GetFileName(testApplicationModuleInfo.GetProcessPath()) - : testApplicationModuleInfo.IsCurrentTestApplicationHostDotnetMuxer - ? $"dotnet exec {Path.GetFileName(testApplicationModuleInfo.GetCurrentTestApplicationFullPath())}" - : PlatformResources.HelpTestApplicationRunner; - - async Task PrintOptionsAsync(IEnumerable optionProviders, int leftPaddingDepth, bool builtInOnly = false) - { - IEnumerable options = - optionProviders - .SelectMany(provider => provider.GetCommandLineOptions()) - .Where(option => !option.IsHidden) - .OrderBy(option => option.Name); - - options = builtInOnly ? options.Where(option => option.IsBuiltIn) : options.Where(option => !option.IsBuiltIn); - - if (!options.Any()) - { - return false; - } - - int maxOptionNameLength = options.Max(option => option.Name.Length); + internal IReadOnlyCollection ExtensionsCommandLineOptionsProviders { get; } - foreach (CommandLineOption? option in options) - { - await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{new string(' ', leftPaddingDepth * 2)}--{option.Name}{new string(' ', maxOptionNameLength - option.Name.Length)} {option.Description}")); - } + internal IReadOnlyCollection SystemCommandLineOptionsProviders { get; } - return options.Any(); - } - - async Task PrintApplicationUsageAsync(string applicationName) - { - await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.HelpApplicationUsage, applicationName))); - await _platformOutputDevice.DisplayAsync(this, EmptyText); - await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpExecuteTestApplication)); - await _platformOutputDevice.DisplayAsync(this, EmptyText); - - RoslynDebug.Assert( - !_systemCommandLineOptionsProviders.OfType().Any(), - "System command line options should not have any tool option registered."); - await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpOptions)); - await PrintOptionsAsync(_systemCommandLineOptionsProviders.Union(ExtensionsCommandLineOptionsProviders), 1, builtInOnly: true); - await _platformOutputDevice.DisplayAsync(this, EmptyText); - - await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpExtensionOptions)); - if (!await PrintOptionsAsync(ExtensionsCommandLineOptionsProviders.Where(provider => provider is not IToolCommandLineOptionsProvider), 1)) - { - await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpNoExtensionRegistered)); - } - - await _platformOutputDevice.DisplayAsync(this, EmptyText); - } - - // Temporary disabled, we don't remove the code because could be useful in future. - // void PrintApplicationToolUsage(ITool[]? availableTools, string applicationName) - // { - // _console.WriteLine($"Usage {applicationName} [tool-name] [tool-optionProviders]"); - // _console.WriteLine(); - // _console.WriteLine("Execute a .NET Test Application tool."); - // _console.WriteLine(); - // _console.WriteLine("Tools:"); - // var tools = availableTools - // ?.Where(tool => !tool.Hidden) - // .OrderBy(tool => tool.DisplayName) - // .ToList(); - // if (tools is null || tools.Count == 0) - // { - // _console.WriteLine("No tools registered."); - // return; - // } - // int maxToolNameLength = tools.Max(tool => tool.Name.Length); - // foreach (ITool tool in tools) - // { - // _console.WriteLine($" {tool.Name}{new string(' ', maxToolNameLength - tool.Name.Length)} ({tool.DisplayName}): {tool.Description}"); - // PrintOptions(ExtensionsCommandLineOptionsProviders.Where(provider => provider is IToolCommandLineOptionsProvider), 2); - // } - // } - } + internal CommandLineParseResult ParseResult { get; } public async Task PrintInfoAsync(ITool[]? availableTools = null) { @@ -493,7 +144,8 @@ async Task DisplayOptionsAsync(IEnumerable options, int inden } else { - await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{optionInfoIndent}Arity: {option.Arity.Min}..{option.Arity.Max}")); + string maxArityValue = option.Arity.Max == int.MaxValue ? "N" : $"{option.Arity.Max}"; + await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{optionInfoIndent}Arity: {option.Arity.Min}..{maxArityValue}")); } await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{optionInfoIndent}Hidden: {option.IsHidden}")); @@ -528,13 +180,13 @@ async Task DisplayProvidersAsync(IEnumerable option async Task DisplayBuiltInExtensionsInfoAsync() { await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData("Built-in command line providers:")); - if (_systemCommandLineOptionsProviders.Length == 0) + if (SystemCommandLineOptionsProviders.Count == 0) { await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(" There are no built-in command line providers.")); } else { - await DisplayProvidersAsync(_systemCommandLineOptionsProviders, 1); + await DisplayProvidersAsync(SystemCommandLineOptionsProviders, 1); } } @@ -569,20 +221,132 @@ async Task DisplayRegisteredToolsInfoAsync(ITool[]? availableTools, List? providers)) + { + await DisplayProvidersAsync(providers, 3); + } + else + { + await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(" There are no registered command line providers.")); + } } } } } public bool IsOptionSet(string optionName) - => _parseResult?.IsOptionSet(optionName) == true; + => ParseResult.IsOptionSet(optionName); public bool TryGetOptionArgumentList(string optionName, [NotNullWhen(true)] out string[]? arguments) { arguments = null; - return _parseResult is not null && _parseResult.TryGetOptionArgumentList(optionName, out arguments); + return ParseResult is not null && ParseResult.TryGetOptionArgumentList(optionName, out arguments); } public Task IsEnabledAsync() => Task.FromResult(false); + + public bool IsHelpInvoked() => IsOptionSet(PlatformCommandLineProvider.HelpOptionKey); + + public bool IsInfoInvoked() => IsOptionSet(PlatformCommandLineProvider.InfoOptionKey); + + public bool IsDotNetTestPipeInvoked() => IsOptionSet(PlatformCommandLineProvider.DotNetTestPipeOptionKey); + +#pragma warning disable IDE0060 // Remove unused parameter, temporary we don't use it. + public async Task PrintHelpAsync(ITool[]? availableTools = null) +#pragma warning restore IDE0060 // Remove unused parameter + { + string applicationName = GetApplicationName(_testApplicationModuleInfo); + await PrintApplicationUsageAsync(applicationName); + + // Temporary disabled, we don't remove the code because could be useful in future. + // PrintApplicationToolUsage(availableTools, applicationName); + await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Empty)); + + // Local functions + static string GetApplicationName(ITestApplicationModuleInfo testApplicationModuleInfo) + => testApplicationModuleInfo.IsAppHostOrSingleFileOrNativeAot + ? Path.GetFileName(testApplicationModuleInfo.GetProcessPath()) + : testApplicationModuleInfo.IsCurrentTestApplicationHostDotnetMuxer + ? $"dotnet exec {Path.GetFileName(testApplicationModuleInfo.GetCurrentTestApplicationFullPath())}" + : PlatformResources.HelpTestApplicationRunner; + + async Task PrintOptionsAsync(IEnumerable optionProviders, int leftPaddingDepth, + bool builtInOnly = false) + { + CommandLineOption[] options = + optionProviders + .SelectMany(provider => provider.GetCommandLineOptions()) + .Where(option => !option.IsHidden && option.IsBuiltIn == builtInOnly) + .OrderBy(option => option.Name) + .ToArray(); + + if (options.Length == 0) + { + return false; + } + + int maxOptionNameLength = options.Max(option => option.Name.Length); + + foreach (CommandLineOption? option in options) + { + await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{new string(' ', leftPaddingDepth * 2)}--{option.Name}{new string(' ', maxOptionNameLength - option.Name.Length)} {option.Description}")); + } + + return options.Length != 0; + } + + async Task PrintApplicationUsageAsync(string applicationName) + { + await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.HelpApplicationUsage, applicationName))); + await _platformOutputDevice.DisplayAsync(this, EmptyText); + await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpExecuteTestApplication)); + await _platformOutputDevice.DisplayAsync(this, EmptyText); + + RoslynDebug.Assert( + !SystemCommandLineOptionsProviders.OfType().Any(), + "System command line options should not have any tool option registered."); + await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpOptions)); + ICommandLineOptionsProvider[] nonToolsExtensionProviders = + ExtensionsCommandLineOptionsProviders + .Where(provider => provider is not IToolCommandLineOptionsProvider) + .ToArray(); + // By default, only system options are built-in but some extensions (e.g. retry) are considered as built-in too, + // so we need to union the 2 collections before printing the options. + await PrintOptionsAsync(SystemCommandLineOptionsProviders.Union(nonToolsExtensionProviders), 1, builtInOnly: true); + await _platformOutputDevice.DisplayAsync(this, EmptyText); + + await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpExtensionOptions)); + if (!await PrintOptionsAsync(nonToolsExtensionProviders, 1)) + { + await _platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpNoExtensionRegistered)); + } + + await _platformOutputDevice.DisplayAsync(this, EmptyText); + } + + // Temporary disabled, we don't remove the code because could be useful in future. + // void PrintApplicationToolUsage(ITool[]? availableTools, string applicationName) + // { + // _console.WriteLine($"Usage {applicationName} [tool-name] [tool-optionProviders]"); + // _console.WriteLine(); + // _console.WriteLine("Execute a .NET Test Application tool."); + // _console.WriteLine(); + // _console.WriteLine("Tools:"); + // var tools = availableTools + // ?.Where(tool => !tool.Hidden) + // .OrderBy(tool => tool.DisplayName) + // .ToList(); + // if (tools is null || tools.Count == 0) + // { + // _console.WriteLine("No tools registered."); + // return; + // } + // int maxToolNameLength = tools.Max(tool => tool.Name.Length); + // foreach (ITool tool in tools) + // { + // _console.WriteLine($" {tool.Name}{new string(' ', maxToolNameLength - tool.Name.Length)} ({tool.DisplayName}): {tool.Description}"); + // PrintOptions(ExtensionsCommandLineOptionsProviders.Where(provider => provider is IToolCommandLineOptionsProvider), 2); + // } + // } + } } diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineManager.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineManager.cs index 9bef4479c5..3dfb8af484 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineManager.cs @@ -6,6 +6,7 @@ using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.OutputDevice; using Microsoft.Testing.Platform.Services; +using Microsoft.Testing.Platform.Tools; namespace Microsoft.Testing.Platform.CommandLine; @@ -23,31 +24,31 @@ public void AddProvider(Func commandLineProviderFac _commandLineProviderFactory.Add(commandLineProviderFactory); } - internal async Task BuildAsync(string[] args, IPlatformOutputDevice platformOutputDisplay, CommandLineParseResult parseResult) + internal async Task BuildAsync(IPlatformOutputDevice platformOutputDisplay, CommandLineParseResult parseResult) { List commandLineOptionsProviders = []; foreach (Func commandLineProviderFactory in _commandLineProviderFactory) { - ICommandLineOptionsProvider serviceInstance = commandLineProviderFactory(); - if (!await serviceInstance.IsEnabledAsync()) + ICommandLineOptionsProvider commandLineOptionsProvider = commandLineProviderFactory(); + if (!await commandLineOptionsProvider.IsEnabledAsync()) { continue; } - if (serviceInstance is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await commandLineOptionsProvider.TryInitializeAsync(); - commandLineOptionsProviders.Add(new CommandLineOptionsProviderCache(serviceInstance)); + commandLineOptionsProviders.Add( + commandLineOptionsProvider is IToolCommandLineOptionsProvider toolCommandLineOptionsProvider + ? new ToolCommandLineOptionsProviderCache(toolCommandLineOptionsProvider) + : new CommandLineOptionsProviderCache(commandLineOptionsProvider)); } - ICommandLineOptionsProvider[] systemCommandLineOptionsProviders = new[] - { - new PlatformCommandLineProvider(), - }; + ICommandLineOptionsProvider[] systemCommandLineOptionsProviders = + [ + new PlatformCommandLineProvider() + ]; - return new CommandLineHandler(args, parseResult, commandLineOptionsProviders.ToArray(), + return new CommandLineHandler(parseResult, commandLineOptionsProviders.ToArray(), systemCommandLineOptionsProviders, _testApplicationModuleInfo, _runtimeFeature, platformOutputDisplay, _environment, _processHandler); } } diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsProviderCache.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsProviderCache.cs index 2b7eac2e7e..20abdd438a 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsProviderCache.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsProviderCache.cs @@ -9,7 +9,7 @@ namespace Microsoft.Testing.Platform.CommandLine; internal struct CommandLineOptionsProviderCache(ICommandLineOptionsProvider commandLineOptionsProvider) : ICommandLineOptionsProvider { private readonly ICommandLineOptionsProvider _commandLineOptionsProvider = commandLineOptionsProvider; - private CommandLineOption[]? _commandLineOptions; + private IReadOnlyCollection? _commandLineOptions; public readonly string Uid => _commandLineOptionsProvider.Uid; @@ -21,7 +21,7 @@ internal struct CommandLineOptionsProviderCache(ICommandLineOptionsProvider comm public IReadOnlyCollection GetCommandLineOptions() { - _commandLineOptions ??= _commandLineOptionsProvider.GetCommandLineOptions().ToArray(); + _commandLineOptions ??= _commandLineOptionsProvider.GetCommandLineOptions(); return _commandLineOptions; } diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs new file mode 100644 index 0000000000..56fa01c467 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs @@ -0,0 +1,253 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; +using System.Text; + +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Resources; + +namespace Microsoft.Testing.Platform.CommandLine; + +internal static class CommandLineOptionsValidator +{ + public static async Task ValidateAsync( + CommandLineParseResult commandLineParseResult, + IEnumerable systemCommandLineOptionsProviders, + IEnumerable extensionCommandLineOptionsProviders, + ICommandLineOptions commandLineOptions) + { + if (commandLineParseResult.HasError) + { + StringBuilder stringBuilder = new(); + stringBuilder.AppendLine(PlatformResources.InvalidCommandLineArguments); + foreach (string error in commandLineParseResult.Errors) + { + stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"\t- {error}"); + } + + return ValidationResult.Invalid(stringBuilder.ToTrimmedString()); + } + + var extensionOptionsByProvider = extensionCommandLineOptionsProviders.ToDictionary(p => p, p => p.GetCommandLineOptions()); + if (ValidateExtensionOptionsDoNotContainReservedPrefix(extensionOptionsByProvider) is { IsValid: false } result) + { + return result; + } + + var systemOptionsByProvider = systemCommandLineOptionsProviders.ToDictionary(p => p, p => p.GetCommandLineOptions()); + if (ValidateExtensionOptionsDoNotContainReservedOptions(extensionOptionsByProvider, systemOptionsByProvider) is { IsValid: false } result2) + { + return result2; + } + + if (ValidateOptionsAreNotDuplicated(extensionOptionsByProvider) is { IsValid: false } result3) + { + return result3; + } + + if (ValidateNoUnknownOptions(commandLineParseResult, extensionOptionsByProvider, systemOptionsByProvider) is { IsValid: false } result4) + { + return result4; + } + + var providerAndOptionByOptionName = extensionOptionsByProvider.Union(systemOptionsByProvider) + .SelectMany(tuple => tuple.Value.Select(option => (provider: tuple.Key, option))) + .ToDictionary(tuple => tuple.option.Name); + + if (ValidateOptionsArgumentArity(commandLineParseResult, providerAndOptionByOptionName) is { IsValid: false } result5) + { + return result5; + } + + if (await ValidateOptionsArgumentsAsync(commandLineParseResult, providerAndOptionByOptionName) is { IsValid: false } result6) + { + return result6; + } + + // Last validation step + return await ValidateConfigurationAsync(extensionOptionsByProvider.Keys, systemOptionsByProvider.Keys, commandLineOptions); + } + + private static ValidationResult ValidateExtensionOptionsDoNotContainReservedPrefix( + Dictionary> extensionOptionsByProvider) + { + StringBuilder? stringBuilder = null; + foreach (KeyValuePair> providerWithOptions in extensionOptionsByProvider) + { + foreach (CommandLineOption option in providerWithOptions.Value) + { + if (option.IsBuiltIn) + { + continue; + } + + string trimmedOption = option.Name.Trim(CommandLineParseResult.OptionPrefix); + if (trimmedOption.StartsWith("internal", StringComparison.OrdinalIgnoreCase) + || option.Name.StartsWith("-internal", StringComparison.OrdinalIgnoreCase)) + { + stringBuilder ??= new(); + ICommandLineOptionsProvider commandLineOptionsProvider = providerWithOptions.Key; + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionIsUsingReservedPrefix, trimmedOption, commandLineOptionsProvider.DisplayName, commandLineOptionsProvider.Uid)); + } + } + } + + return stringBuilder?.Length > 0 + ? ValidationResult.Invalid(stringBuilder.ToTrimmedString()) + : ValidationResult.Valid(); + } + + private static ValidationResult ValidateExtensionOptionsDoNotContainReservedOptions( + Dictionary> extensionOptionsByProvider, + Dictionary> systemOptionsByProvider) + { + IEnumerable allExtensionOptions = extensionOptionsByProvider.Values.SelectMany(x => x).Select(x => x.Name).Distinct(); + IEnumerable allSystemOptions = systemOptionsByProvider.Values.SelectMany(x => x).Select(x => x.Name).Distinct(); + + IEnumerable invalidReservedOptions = allSystemOptions.Intersect(allExtensionOptions); + if (invalidReservedOptions.Any()) + { + var stringBuilder = new StringBuilder(); + foreach (string reservedOption in invalidReservedOptions) + { + IEnumerable faultyProviderNames = extensionOptionsByProvider.Where(tuple => tuple.Value.Any(x => x.Name == reservedOption)).Select(tuple => tuple.Key.DisplayName); + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionIsReserved, reservedOption, string.Join("', '", faultyProviderNames))); + } + + return ValidationResult.Invalid(stringBuilder.ToTrimmedString()); + } + + return ValidationResult.Valid(); + } + + private static ValidationResult ValidateOptionsAreNotDuplicated( + Dictionary> extensionOptionsByProvider) + { + IEnumerable duplications = extensionOptionsByProvider.Values.SelectMany(x => x) + .Select(x => x.Name) + .GroupBy(x => x) + .Where(x => x.Skip(1).Any()) + .Select(x => x.Key); + + StringBuilder? stringBuilder = null; + foreach (string duplicatedOption in duplications) + { + IEnumerable faultyProvidersDisplayNames = extensionOptionsByProvider.Where(tuple => tuple.Value.Any(x => x.Name == duplicatedOption)).Select(tuple => tuple.Key.DisplayName); + stringBuilder ??= new(); + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionIsDeclaredByMultipleProviders, duplicatedOption, string.Join("', '", faultyProvidersDisplayNames))); + } + + return stringBuilder?.Length > 0 + ? ValidationResult.Invalid(stringBuilder.ToTrimmedString()) + : ValidationResult.Valid(); + } + + private static ValidationResult ValidateNoUnknownOptions( + CommandLineParseResult parseResult, + Dictionary> extensionOptionsByProvider, + Dictionary> systemOptionsByProvider) + { + StringBuilder? stringBuilder = null; + foreach (OptionRecord optionRecord in parseResult.Options) + { + if (!extensionOptionsByProvider.Union(systemOptionsByProvider).Any(tuple => tuple.Value.Any(x => x.Name == optionRecord.Option))) + { + stringBuilder ??= new(); + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineUnknownOption, optionRecord.Option)); + } + } + + return stringBuilder?.Length > 0 + ? ValidationResult.Invalid(stringBuilder.ToTrimmedString()) + : ValidationResult.Valid(); + } + + private static ValidationResult ValidateOptionsArgumentArity( + CommandLineParseResult parseResult, + Dictionary providerAndOptionByOptionName) + { + StringBuilder stringBuilder = new(); + foreach (IGrouping groupedOptions in parseResult.Options.GroupBy(x => x.Option)) + { + // getting the arguments count for an option. + int arity = 0; + foreach (OptionRecord optionEntry in groupedOptions) + { + arity += optionEntry.Arguments.Length; + } + + string optionName = groupedOptions.Key; + (ICommandLineOptionsProvider provider, CommandLineOption option) = providerAndOptionByOptionName[optionName]; + + if (arity > option.Arity.Max && option.Arity.Max == 0) + { + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionExpectsNoArguments, optionName, provider.DisplayName, provider.Uid)); + } + else if (arity < option.Arity.Min) + { + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionExpectsAtLeastArguments, optionName, provider.DisplayName, provider.Uid, option.Arity.Min)); + } + else if (arity > option.Arity.Max) + { + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineOptionExpectsAtMostArguments, optionName, provider.DisplayName, provider.Uid, option.Arity.Max)); + } + } + + return stringBuilder.Length > 0 + ? ValidationResult.Invalid(stringBuilder.ToTrimmedString()) + : ValidationResult.Valid(); + } + + private static async Task ValidateOptionsArgumentsAsync( + CommandLineParseResult parseResult, + Dictionary providerAndOptionByOptionName) + { + ApplicationStateGuard.Ensure(parseResult is not null); + + StringBuilder? stringBuilder = null; + foreach (OptionRecord optionRecord in parseResult.Options) + { + (ICommandLineOptionsProvider provider, CommandLineOption option) = providerAndOptionByOptionName[optionRecord.Option]; + ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, optionRecord.Arguments); + if (!result.IsValid) + { + stringBuilder ??= new(); + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineInvalidArgumentsForOption, optionRecord.Option, result.ErrorMessage)); + } + } + + return stringBuilder?.Length > 0 + ? ValidationResult.Invalid(stringBuilder.ToTrimmedString()) + : ValidationResult.Valid(); + } + + private static async Task ValidateConfigurationAsync( + IEnumerable extensionsProviders, + IEnumerable systemProviders, + ICommandLineOptions commandLineOptions) + { + StringBuilder? stringBuilder = null; + foreach (ICommandLineOptionsProvider commandLineOptionsProvider in systemProviders.Union(extensionsProviders)) + { + ValidationResult result = await commandLineOptionsProvider.ValidateCommandLineOptionsAsync(commandLineOptions); + if (!result.IsValid) + { + stringBuilder ??= new(); + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineInvalidConfiguration, commandLineOptionsProvider.DisplayName, commandLineOptionsProvider.Uid, result.ErrorMessage)); + stringBuilder.AppendLine(); + } + } + + return stringBuilder?.Length > 0 + ? ValidationResult.Invalid(stringBuilder.ToTrimmedString()) + : ValidationResult.Valid(); + } + + private static string ToTrimmedString(this StringBuilder stringBuilder) +#pragma warning disable RS0030 // Do not use banned APIs + => stringBuilder.ToString().TrimEnd(Environment.NewLine.ToCharArray()); +#pragma warning restore RS0030 // Do not use banned APIs +} diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineHandler.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineHandler.cs index f61ca585fc..0d19a2fa42 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineHandler.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineHandler.cs @@ -7,11 +7,7 @@ namespace Microsoft.Testing.Platform.CommandLine; internal interface ICommandLineHandler { - string[] Arguments { get; } - bool IsHelpInvoked(); Task PrintHelpAsync(ITool[]? availableTools = null); - - Task<(bool IsValid, string? ValidationError)> TryParseAndValidateAsync(); } diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/InformativeCommandLineTestHost.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/InformativeCommandLineTestHost.cs index 0e30ee886d..833f679e99 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/InformativeCommandLineTestHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/InformativeCommandLineTestHost.cs @@ -2,12 +2,31 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Testing.Platform.Hosts; +using Microsoft.Testing.Platform.IPC; namespace Microsoft.Testing.Platform.CommandLine; -internal sealed class InformativeCommandLineTestHost(int returnValue) : ITestHost +internal sealed class InformativeCommandLineTestHost(int returnValue, NamedPipeClient? dotnetTestPipe = null) : ITestHost, IDisposable +#if NETCOREAPP +#pragma warning disable SA1001 // Commas should be spaced correctly + , IAsyncDisposable +#pragma warning restore SA1001 // Commas should be spaced correctly +#endif { private readonly int _returnValue = returnValue; + private readonly NamedPipeClient? _dotnetTestPipeClient = dotnetTestPipe; public Task RunAsync() => Task.FromResult(_returnValue); + + public void Dispose() => _dotnetTestPipeClient?.Dispose(); + +#if NETCOREAPP + public async ValueTask DisposeAsync() + { + if (_dotnetTestPipeClient is not null) + { + await _dotnetTestPipeClient.DisposeAsync(); + } + } +#endif } diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/ParseResult.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/ParseResult.cs index a2e21f9d3d..9d965377db 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/ParseResult.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/ParseResult.cs @@ -5,21 +5,21 @@ namespace Microsoft.Testing.Platform.CommandLine; -internal sealed class CommandLineParseResult(string? toolName, OptionRecord[] options, string[] errors, string[] originalArguments) : IEquatable +internal sealed class CommandLineParseResult(string? toolName, IReadOnlyList options, IReadOnlyList errors, IReadOnlyList originalArguments) : IEquatable { public const char OptionPrefix = '-'; public static CommandLineParseResult Empty => new(null, [], [], []); - public string? ToolName { get; set; } = toolName; + public string? ToolName { get; } = toolName; - public OptionRecord[] Options { get; } = options; + public IReadOnlyList Options { get; } = options; - public string[] Errors { get; } = errors; + public IReadOnlyList Errors { get; } = errors; - public string[] OriginalArguments { get; } = originalArguments; + public IReadOnlyList OriginalArguments { get; } = originalArguments; - public bool HasError => Errors.Length > 0; + public bool HasError => Errors.Count > 0; public bool HasTool => ToolName is not null; @@ -40,12 +40,12 @@ public bool Equals(CommandLineParseResult? other) return false; } - if (Errors.Length != other.Errors.Length) + if (Errors.Count != other.Errors.Count) { return false; } - for (int i = 0; i < Errors.Length; i++) + for (int i = 0; i < Errors.Count; i++) { if (Errors[i] != other.Errors[i]) { @@ -53,14 +53,14 @@ public bool Equals(CommandLineParseResult? other) } } - if (Options.Length != other.Options.Length) + if (Options.Count != other.Options.Count) { return false; } - OptionRecord[] thisOptions = Options; - OptionRecord[] otherOptions = other.Options; - for (int i = 0; i < thisOptions.Length; i++) + IReadOnlyList thisOptions = Options; + IReadOnlyList otherOptions = other.Options; + for (int i = 0; i < thisOptions.Count; i++) { if (thisOptions[i].Option != otherOptions[i].Option) { @@ -89,15 +89,15 @@ public bool IsOptionSet(string optionName) public bool TryGetOptionArgumentList(string optionName, [NotNullWhen(true)] out string[]? arguments) { - arguments = null; - - IEnumerable? result = Options.Where(x => x.Option == optionName.Trim(OptionPrefix)); - if (result?.Any() == true) + optionName = optionName.Trim(OptionPrefix); + IEnumerable result = Options.Where(x => x.Option == optionName); + if (result.Any()) { arguments = result.SelectMany(x => x.Arguments).ToArray(); return true; } + arguments = null; return false; } diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/Parser.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/Parser.cs index 425926c3b3..074d05cb5a 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/Parser.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/Parser.cs @@ -118,24 +118,17 @@ public static CommandLineParseResult Parse(string[] args, IEnvironment environme options.Add(new(currentOption, currentOptionArguments.ToArray())); } - return new CommandLineParseResult(toolName, options.ToArray(), errors.ToArray(), args); + return new CommandLineParseResult(toolName, options, errors, args); static void ParseOptionAndSeparators(string arg, out string? currentOption, out string? currentArg) { - currentArg = null; - (int delimiterIndex, string delimiterFound) = new List<(int, string)> + (currentOption, currentArg) = arg.IndexOfAny([':', '=', ' ']) switch { - (arg.IndexOf(":", StringComparison.InvariantCultureIgnoreCase), ":"), - (arg.IndexOf("=", StringComparison.InvariantCultureIgnoreCase), "="), - (arg.IndexOf(" ", StringComparison.InvariantCultureIgnoreCase), " "), - }.Where(x => x.Item1 != -1).OrderBy(x => x.Item1).FirstOrDefault(); + -1 => (arg, null), + var delimiterIndex => (arg[..delimiterIndex], arg[(delimiterIndex + 1)..]), + }; - currentOption = arg[..(RoslynString.IsNullOrEmpty(delimiterFound) ? arg.Length : arg.IndexOf(delimiterFound, StringComparison.InvariantCultureIgnoreCase))].TrimStart('-'); - - if (!RoslynString.IsNullOrEmpty(delimiterFound)) - { - currentArg = arg[(arg.IndexOf(delimiterFound, StringComparison.InvariantCultureIgnoreCase) + 1)..]; - } + currentOption = currentOption.TrimStart('-'); } static bool TryUnescape(string input, string? option, IEnvironment environment, out string? unescapedArg, out string? error) diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/PlatformCommandLineProvider.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/PlatformCommandLineProvider.cs index 648e25c179..e49db38ff7 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/PlatformCommandLineProvider.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/PlatformCommandLineProvider.cs @@ -20,10 +20,6 @@ internal sealed class PlatformCommandLineProvider : ICommandLineOptionsProvider public const string DiagnosticOutputDirectoryOptionKey = "diagnostic-output-directory"; public const string DiagnosticVerbosityOptionKey = "diagnostic-verbosity"; public const string DiagnosticFileLoggerSynchronousWriteOptionKey = "diagnostic-filelogger-synchronouswrite"; - public const string ServerOptionKey = "server"; - public const string PortOptionKey = "port"; - public const string ClientPortOptionKey = "client-port"; - public const string ClientHostOptionKey = "client-host"; public const string VSTestAdapterModeOptionKey = "internal-vstest-adapter"; public const string NoBannerOptionKey = "no-banner"; public const string SkipBuildersNumberCheckOptionKey = "internal-testingplatform-skipbuildercheck"; @@ -34,10 +30,18 @@ internal sealed class PlatformCommandLineProvider : ICommandLineOptionsProvider public const string TestHostControllerPIDOptionKey = "internal-testhostcontroller-pid"; public const string ExitOnProcessExitOptionKey = "exit-on-process-exit"; + public const string ServerOptionKey = "server"; + public const string PortOptionKey = "port"; + public const string ClientPortOptionKey = "client-port"; + public const string ClientHostOptionKey = "client-host"; + public const string DotNetTestPipeOptionKey = "dotnet-test-pipe"; + + private static readonly string[] VerbosityOptions = ["Trace", "Debug", "Information", "Warning", "Error", "Critical"]; + private static readonly CommandLineOption MinimumExpectedTests = new(MinimumExpectedTestsOptionKey, "Specifies the minimum number of tests that are expected to run.", ArgumentArity.ZeroOrOne, false, isBuiltIn: true); - private static readonly IReadOnlyCollection PlatformCommandLineProviderCache = new[] - { + private static readonly IReadOnlyCollection PlatformCommandLineProviderCache = + [ // Visible options new(HelpOptionKey, PlatformResources.PlatformCommandLineHelpOptionDescription, ArgumentArity.Zero, false, isBuiltIn: true), new(InfoOptionKey, PlatformResources.PlatformCommandLineInfoOptionDescription, ArgumentArity.Zero, false, isBuiltIn: true), @@ -53,7 +57,7 @@ internal sealed class PlatformCommandLineProvider : ICommandLineOptionsProvider new(ExitOnProcessExitOptionKey, PlatformResources.PlatformCommandLineExitOnProcessExitOptionDescription, ArgumentArity.ExactlyOne, false, isBuiltIn: true), // Hidden options - new(ServerOptionKey, PlatformResources.PlatformCommandLineServerOptionDescription, ArgumentArity.Zero, true, isBuiltIn: true), + new(ServerOptionKey, PlatformResources.PlatformCommandLineServerOptionDescription, ArgumentArity.ZeroOrOne, true, isBuiltIn: true), new(PortOptionKey, PlatformResources.PlatformCommandLinePortOptionDescription, ArgumentArity.ExactlyOne, true, isBuiltIn: true), new(ClientPortOptionKey, PlatformResources.PlatformCommandLineClientPortOptionDescription, ArgumentArity.ExactlyOne, true, isBuiltIn: true), new(ClientHostOptionKey, PlatformResources.PlatformCommandLineClientHostOptionDescription, ArgumentArity.ExactlyOne, true, isBuiltIn: true), @@ -61,7 +65,8 @@ internal sealed class PlatformCommandLineProvider : ICommandLineOptionsProvider new(VSTestAdapterModeOptionKey, PlatformResources.PlatformCommandLineVSTestAdapterModeOptionDescription, ArgumentArity.Zero, true, isBuiltIn: true), new(NoBannerOptionKey, PlatformResources.PlatformCommandLineNoBannerOptionDescription, ArgumentArity.ZeroOrOne, true, isBuiltIn: true), new(TestHostControllerPIDOptionKey, PlatformResources.PlatformCommandLineTestHostControllerPIDOptionDescription, ArgumentArity.ZeroOrOne, true, isBuiltIn: true), - }; + new(DotNetTestPipeOptionKey, PlatformResources.PlatformCommandLineDotnetTestPipe, ArgumentArity.ExactlyOne, true, isBuiltIn: true) + ]; /// public string Uid { get; } = nameof(PlatformCommandLineProvider); @@ -83,61 +88,25 @@ public IReadOnlyCollection GetCommandLineOptions() public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) { - if (commandOption.Name == HelpOptionKey && arguments.Length > 0) - { - return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineOptionExpectsNoArgumentErrorMessage, HelpOptionKey)); - } - - if (commandOption.Name == InfoOptionKey && arguments.Length > 0) - { - return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineOptionExpectsNoArgumentErrorMessage, InfoOptionKey)); - } - if (commandOption.Name == DiagnosticVerbosityOptionKey) { - if (arguments.Length != 1 - || (!arguments[0].Equals("Trace", StringComparison.OrdinalIgnoreCase) - && !arguments[0].Equals("Debug", StringComparison.OrdinalIgnoreCase) - && !arguments[0].Equals("Information", StringComparison.OrdinalIgnoreCase) - && !arguments[0].Equals("Warning", StringComparison.OrdinalIgnoreCase) - && !arguments[0].Equals("Error", StringComparison.OrdinalIgnoreCase) - && !arguments[0].Equals("Critical", StringComparison.OrdinalIgnoreCase))) + if (!VerbosityOptions.Contains(arguments[0], StringComparer.OrdinalIgnoreCase)) { return ValidationResult.InvalidTask(PlatformResources.PlatformCommandLineDiagnosticOptionExpectsSingleArgumentErrorMessage); } } - if (commandOption.Name == DiagnosticOutputDirectoryOptionKey && arguments.Length != 1) - { - return ValidationResult.InvalidTask(PlatformResources.PlatformCommandLineDiagnosticOutputDirectoryOptionSingleArgument); - } - - if (commandOption.Name == DiagnosticOutputFilePrefixOptionKey && arguments.Length != 1) - { - return ValidationResult.InvalidTask(PlatformResources.PlatformCommandLineDiagnosticFilePrefixOptionSingleArgument); - } - - if (commandOption.Name == ResultDirectoryOptionKey && arguments.Length != 1) - { - return ValidationResult.InvalidTask($"Invalid arguments for --{ResultDirectoryOptionKey}, expected usage: --results-directory ./CustomTestResultsFolder"); - } - - if (commandOption.Name == PortOptionKey && (arguments.Length != 1 || !int.TryParse(arguments[0], out int _))) + if (commandOption.Name == PortOptionKey && (!int.TryParse(arguments[0], out int _))) { return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLinePortOptionSingleArgument, PortOptionKey)); } - if (commandOption.Name == ClientPortOptionKey && (arguments.Length != 1 || !int.TryParse(arguments[0], out int _))) + if (commandOption.Name == ClientPortOptionKey && (!int.TryParse(arguments[0], out int _))) { return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLinePortOptionSingleArgument, ClientPortOptionKey)); } - if (commandOption.Name == ClientHostOptionKey && arguments.Length != 1) - { - return ValidationResult.InvalidTask(PlatformResources.PlatformCommandLineClientHostOptionSingleArgument); - } - - if (commandOption.Name == ExitOnProcessExitOptionKey && (arguments.Length != 1 || !int.TryParse(arguments[0], out int _))) + if (commandOption.Name == ExitOnProcessExitOptionKey && (!int.TryParse(arguments[0], out int _))) { return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineExitOnProcessExitSingleArgument, ExitOnProcessExitOptionKey)); } @@ -187,7 +156,7 @@ public Task ValidateCommandLineOptionsAsync(ICommandLineOption if (commandLineOptions.IsOptionSet(ExitOnProcessExitOptionKey)) { - commandLineOptions.TryGetOptionArgumentList(ExitOnProcessExitOptionKey, out string[]? pid); + _ = commandLineOptions.TryGetOptionArgumentList(ExitOnProcessExitOptionKey, out string[]? pid); ApplicationStateGuard.Ensure(pid is not null); RoslynDebug.Assert(pid.Length == 1); int parentProcessPid = int.Parse(pid[0], CultureInfo.InvariantCulture); @@ -195,11 +164,11 @@ public Task ValidateCommandLineOptionsAsync(ICommandLineOption { // We let the api to do the validity check before to go down the subscription path. // If we don't fail here but we fail below means that the parent process is not there anymore and we can take it as exited. - Process.GetProcessById(parentProcessPid); + _ = Process.GetProcessById(parentProcessPid); } - catch (Exception ex) + catch (ArgumentException ex) { - return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineExitOnProcessExitInvalidDependantProcess, parentProcessPid, ex)); + return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineExitOnProcessExitInvalidDependentProcess, parentProcessPid, ex)); } } diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/ToolCommandLineOptionsProviderCache.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/ToolCommandLineOptionsProviderCache.cs new file mode 100644 index 0000000000..010b53b00f --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/ToolCommandLineOptionsProviderCache.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Tools; + +namespace Microsoft.Testing.Platform.CommandLine; + +internal struct ToolCommandLineOptionsProviderCache(IToolCommandLineOptionsProvider commandLineOptionsProvider) : IToolCommandLineOptionsProvider +{ + private readonly IToolCommandLineOptionsProvider _commandLineOptionsProvider = commandLineOptionsProvider; + private IReadOnlyCollection? _commandLineOptions; + + public readonly string Uid => _commandLineOptionsProvider.Uid; + + public readonly string Version => _commandLineOptionsProvider.Version; + + public readonly string DisplayName => _commandLineOptionsProvider.DisplayName; + + public readonly string Description => _commandLineOptionsProvider.Description; + + public readonly string ToolName => _commandLineOptionsProvider.ToolName; + + public IReadOnlyCollection GetCommandLineOptions() + { + _commandLineOptions ??= _commandLineOptionsProvider.GetCommandLineOptions(); + + return _commandLineOptions; + } + + public readonly Task IsEnabledAsync() + => _commandLineOptionsProvider.IsEnabledAsync(); + + public readonly Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) + => _commandLineOptionsProvider.ValidateCommandLineOptionsAsync(commandLineOptions); + + public readonly Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) + => _commandLineOptionsProvider.ValidateOptionArgumentsAsync(commandOption, arguments); +} diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/TreeNodeFilterCommandLineOptionsProvider.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/TreeNodeFilterCommandLineOptionsProvider.cs index c4745273b2..f21111e970 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/TreeNodeFilterCommandLineOptionsProvider.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/TreeNodeFilterCommandLineOptionsProvider.cs @@ -28,21 +28,13 @@ internal sealed class TreeNodeFilterCommandLineOptionsProvider(IExtension extens public Task IsEnabledAsync() => Task.FromResult(true); public IReadOnlyCollection GetCommandLineOptions() - => new CommandLineOption[] - { - new(TreenodeFilter, PlatformResources.TreeNodeFilterDescription, ArgumentArity.ZeroOrOne, false), - }; + => + [ + new(TreenodeFilter, PlatformResources.TreeNodeFilterDescription, ArgumentArity.ExactlyOne, false) + ]; public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) - { - if (commandOption.Name == TreenodeFilter && arguments.Length != 1) - { - return ValidationResult.InvalidTask(PlatformResources.TreeNodeFilterInvalidArgumentCount); - } - - // No problem found - return ValidationResult.ValidTask; - } + => ValidationResult.ValidTask; public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) => ValidationResult.ValidTask; diff --git a/src/Platform/Microsoft.Testing.Platform/Configurations/ConfigurationManager.cs b/src/Platform/Microsoft.Testing.Platform/Configurations/ConfigurationManager.cs index 63fac8611e..ccb5691713 100644 --- a/src/Platform/Microsoft.Testing.Platform/Configurations/ConfigurationManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/Configurations/ConfigurationManager.cs @@ -31,10 +31,7 @@ internal async Task BuildAsync(IFileLoggerProvider? syncFileLogg continue; } - if (serviceInstance is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await serviceInstance.TryInitializeAsync(); IConfigurationProvider configurationProvider = serviceInstance.Build(); await configurationProvider.LoadAsync(); @@ -49,14 +46,12 @@ internal async Task BuildAsync(IFileLoggerProvider? syncFileLogg if (syncFileLoggerProvider is not null) { ILogger logger = syncFileLoggerProvider.CreateLogger(nameof(ConfigurationManager)); - if (logger.IsEnabled(LogLevel.Trace)) + if (logger.IsEnabled(LogLevel.Trace) + && defaultJsonConfiguration?.ConfigurationFile != null) { - if (defaultJsonConfiguration is not null && defaultJsonConfiguration.ConfigurationFile is not null) - { - using Stream configFileStream = _fileSystem.NewFileStream(defaultJsonConfiguration.ConfigurationFile, FileMode.Open); - StreamReader streamReader = new(configFileStream); - await logger.LogTraceAsync($"Configuration file ('{defaultJsonConfiguration.ConfigurationFile}') content:\n{await streamReader.ReadToEndAsync()}"); - } + using IFileStream configFileStream = _fileSystem.NewFileStream(defaultJsonConfiguration.ConfigurationFile, FileMode.Open); + StreamReader streamReader = new(configFileStream.Stream); + await logger.LogTraceAsync($"Configuration file ('{defaultJsonConfiguration.ConfigurationFile}') content:\n{await streamReader.ReadToEndAsync()}"); } } diff --git a/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationFileParser.cs b/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationFileParser.cs index 65c1ecfdea..5583122c4d 100644 --- a/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationFileParser.cs +++ b/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationFileParser.cs @@ -36,17 +36,14 @@ public static (Dictionary SingleValueData, Dictionary Task InitializeAsync(); } + +internal static class InitializableExtension +{ + public static async Task TryInitializeAsync(this object target) + { + if (target is IAsyncInitializableExtension initializable) + { + await initializable.InitializeAsync(); + } + } +} diff --git a/src/Platform/Microsoft.Testing.Platform/GlobalSuppressions.cs b/src/Platform/Microsoft.Testing.Platform/GlobalSuppressions.cs new file mode 100644 index 0000000000..6e70346b51 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/GlobalSuppressions.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +// We usually do not want to suppress issues through GlobalSuppressions file but in this case we have to do it because +// we are not able to suppress the issue differently. +#pragma warning disable IDE0076 +[assembly: SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "Source-generated file that cannot match analyzers requirements", Scope = "member", Target = "~M:System.Reflection.NullabilityInfoContext.CheckParameterMetadataType(System.Reflection.ParameterInfo,System.Reflection.NullabilityInfo)")] +#pragma warning restore IDE0076 diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/HashCode.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/HashCode.cs new file mode 100644 index 0000000000..b90376e9ac --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/HashCode.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if !NETCOREAPP +namespace System; + +// https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-overriding-gethashcode/263416#263416 +internal struct HashCode +{ + private int _hash; + + public HashCode() + { + _hash = 17; + } + + public void Add(string value) + { + // Overflow is fine, just wrap + unchecked + { + _hash = (_hash * 23) + (value?.GetHashCode() ?? 0); + } + } + + public void Add(bool value) + { + // Overflow is fine, just wrap + unchecked + { + _hash = (_hash * 23) + value.GetHashCode(); + } + } + + public void Add(int value) + { + // Overflow is fine, just wrap + unchecked + { + _hash = (_hash * 23) + value.GetHashCode(); + } + } + + public readonly int ToHashCode() + => _hash; +} +#endif diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/StringBuilderExtensions.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/StringBuilderExtensions.cs new file mode 100644 index 0000000000..115d107015 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/StringBuilderExtensions.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if !NETCOREAPP +namespace System.Text; + +internal static class StringBuilderExtensions +{ + public static StringBuilder Append(this StringBuilder builder, IFormatProvider? _, string value) + => builder.Append(value); + + public static StringBuilder AppendLine(this StringBuilder builder, IFormatProvider? _, string value) + => builder.AppendLine(value); +} +#endif diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/StringExtensions.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/StringExtensions.cs deleted file mode 100644 index 0aced424c9..0000000000 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/StringExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -namespace Microsoft.Testing.Platform.Helpers; - -internal static class StringExtensions -{ -#if NETSTANDARD - public static bool StartsWith(this string s, char c) - => s.StartsWith(c.ToString(), StringComparison.Ordinal); - - public static bool Contains(this string s, string value, StringComparison comparisonType) - => s.IndexOf(value, comparisonType) >= 0; -#endif -} diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/IFileSystem.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/IFileSystem.cs index 7c7244e3b4..ab6f325b0f 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/IFileSystem.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/IFileSystem.cs @@ -11,9 +11,9 @@ internal interface IFileSystem void Move(string sourceFileName, string destFileName); - Stream NewFileStream(string path, FileMode mode); + IFileStream NewFileStream(string path, FileMode mode); - Stream NewFileStream(string path, FileMode mode, FileAccess access); + IFileStream NewFileStream(string path, FileMode mode, FileAccess access); string ReadAllText(string path); } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/IProcess.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/IProcess.cs index 2ea43eb15a..31abffbaa6 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/IProcess.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/IProcess.cs @@ -3,27 +3,36 @@ namespace Microsoft.Testing.Platform.Helpers; -internal interface IProcess +internal interface IProcess : IDisposable { public event EventHandler Exited; + /// int Id { get; } + /// int ExitCode { get; } + /// bool HasExited { get; } #if NETCOREAPP + /// IMainModule? MainModule { get; } #else + /// IMainModule MainModule { get; } #endif #if NETCOREAPP + /// Task WaitForExitAsync(); #endif + + /// void WaitForExit(); + /// void Kill(); } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/IProcessHandler.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/IProcessHandler.cs index 163b2fc60f..2502a9b078 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/IProcessHandler.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/IProcessHandler.cs @@ -11,5 +11,5 @@ internal interface IProcessHandler IProcess GetCurrentProcess(); - IProcess? Start(ProcessStartInfo startInfo); + IProcess Start(ProcessStartInfo startInfo); } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemFileStream.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemFileStream.cs index c7f69a4c05..5dfd34f685 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemFileStream.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemFileStream.cs @@ -12,6 +12,16 @@ public SystemFileStream(string path, FileMode mode, FileAccess access, FileShare _stream = new FileStream(path, mode, access, share); } + public SystemFileStream(string path, FileMode mode, FileAccess access) + { + _stream = new FileStream(path, mode, access); + } + + public SystemFileStream(string path, FileMode mode) + { + _stream = new FileStream(path, mode); + } + public Stream Stream => _stream; public string Name => _stream.Name; diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemFileSystem.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemFileSystem.cs index f85e131661..6195df50c6 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemFileSystem.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemFileSystem.cs @@ -11,9 +11,9 @@ internal sealed class SystemFileSystem : IFileSystem public void Move(string sourceFileName, string destFileName) => File.Move(sourceFileName, destFileName); - public Stream NewFileStream(string path, FileMode mode) => new FileStream(path, mode); + public IFileStream NewFileStream(string path, FileMode mode) => new SystemFileStream(path, mode); - public Stream NewFileStream(string path, FileMode mode, FileAccess access) => new FileStream(path, mode, access); + public IFileStream NewFileStream(string path, FileMode mode, FileAccess access) => new SystemFileStream(path, mode, access); public string ReadAllText(string path) => File.ReadAllText(path); } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcess.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcess.cs index 5010248237..4d1d5e0d05 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcess.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcess.cs @@ -5,7 +5,7 @@ namespace Microsoft.Testing.Platform.Helpers; -internal sealed class SystemProcess : IProcess +internal sealed class SystemProcess : IProcess, IDisposable { private readonly Process _process; @@ -51,4 +51,6 @@ public void Kill() public void Kill() => _process.Kill(); #endif + + public void Dispose() => _process.Dispose(); } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcessHandler.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcessHandler.cs index ed3fec09d7..6794651a5c 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcessHandler.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcessHandler.cs @@ -3,6 +3,9 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +using Microsoft.Testing.Platform.Resources; namespace Microsoft.Testing.Platform.Helpers; @@ -13,11 +16,13 @@ internal sealed class SystemProcessHandler : IProcessHandler public IProcess GetProcessById(int pid) => new SystemProcess(Process.GetProcessById(pid)); - public IProcess? Start(ProcessStartInfo startInfo) + public IProcess Start(ProcessStartInfo startInfo) { - var process = Process.Start(startInfo); - ApplicationStateGuard.Ensure(process is not null); + Process process = Process.Start(startInfo) + ?? throw new InvalidOperationException(string.Format( + CultureInfo.InvariantCulture, + PlatformResources.CannotStartProcessErrorMessage, startInfo.FileName)); process.EnableRaisingEvents = true; - return process is null ? null : (IProcess)new SystemProcess(process); + return new SystemProcess(process); } } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemTask.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemTask.cs index 8e8424232b..87a9ce109f 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemTask.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemTask.cs @@ -26,7 +26,7 @@ public Task RunLongRunning(Func action, string name, CancellationToken can } TaskCompletionSource taskCompletionSource = new(); - Thread thread = new(new ThreadStart(() => + Thread thread = new(() => { try { @@ -41,7 +41,7 @@ public Task RunLongRunning(Func action, string name, CancellationToken can // will end inside the AppDomain.CurrentDomain.UnhandledException handler as expected throw; } - })) + }) { IsBackground = true, Name = name, diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/SystemPolyfills.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/SystemPolyfills.cs deleted file mode 100644 index 1759ef1201..0000000000 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/SystemPolyfills.cs +++ /dev/null @@ -1,569 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#pragma warning disable SA1201 // Elements should appear in the correct order -#pragma warning disable SA1313 // Parameter names should begin with lower-case letter -#pragma warning disable SA1403 // File may only contain a single namespace -#pragma warning disable SA1502 // Element should not be on a single line -#pragma warning disable SA1623 // Property summary documentation should match accessors -#pragma warning disable SA1642 // Constructor summary documentation should begin with standard text -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved - -#if !NETCOREAPP -using System.ComponentModel; -#if !NET462 -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -using Microsoft.Testing.Platform; -#endif - -namespace System.Runtime.CompilerServices -{ - [EditorBrowsable(EditorBrowsableState.Never)] - internal class IsExternalInit { } - - [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] - internal sealed class CallerArgumentExpressionAttribute : Attribute - { - public CallerArgumentExpressionAttribute(string parameterName) - { - ParameterName = parameterName; - } - - public string ParameterName { get; } - } -} - -namespace System -{ - // https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-overriding-gethashcode/263416#263416 - internal struct HashCode - { - private int _hash; - - public HashCode() - { - _hash = 17; - } - - public void Add(string value) - { - // Overflow is fine, just wrap - unchecked - { - _hash = (_hash * 23) + (value?.GetHashCode() ?? 0); - } - } - - public void Add(bool value) - { - // Overflow is fine, just wrap - unchecked - { - _hash = (_hash * 23) + value.GetHashCode(); - } - } - - public void Add(int value) - { - // Overflow is fine, just wrap - unchecked - { - _hash = (_hash * 23) + value.GetHashCode(); - } - } - - public readonly int ToHashCode() - => _hash; - } - -#if !NET462 - /// Represent a type can be used to index a collection either from the start or the end. - /// - /// Index is used by the C# compiler to support the new index syntax. - /// - /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ; - /// int lastElement = someArray[^1]; // lastElement = 5 - /// - /// - [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Matches implementation from dotnet/runtime")] - [SuppressMessage("Usage", "CA2231:Overload operator equals on overriding value type Equals", Justification = "Matches implementation from dotnet/runtime")] - internal readonly struct Index : IEquatable - { - private readonly int _value; - - /// Initializes a new instance of the struct.Construct an Index using a value and indicating if the index is from the start or from the end. - /// The index value. it has to be zero or positive number. - /// Indicating if the index is from the start or from the end. - /// - /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Index(int value, bool fromEnd = false) - { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), value, "Non-negative number required."); - } - - _value = fromEnd ? ~value : value; - } - - // The following private constructors mainly created for perf reason to avoid the checks - private Index(int value) - { - _value = value; - } - - /// Gets create an Index pointing at first element. - public static Index Start => new(0); - - /// Gets create an Index pointing at beyond last element. - public static Index End => new(~0); - - /// Create an Index from the start at the position indicated by the value. - /// The index value from the start. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Index FromStart(int value) - => value < 0 - ? throw new ArgumentOutOfRangeException(nameof(value), value, "Non-negative number required.") - : new Index(value); - - /// Create an Index from the end at the position indicated by the value. - /// The index value from the end. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Index FromEnd(int value) - => value < 0 - ? throw new ArgumentOutOfRangeException(nameof(value), value, "Non-negative number required.") - : new Index(~value); - - /// Gets the index value. - public int Value => _value < 0 ? ~_value : _value; - - /// Gets a value indicating whether indicates whether the index is from the start or the end. - public bool IsFromEnd => _value < 0; - - /// Calculate the offset from the start using the giving collection length. - /// The length of the collection that the Index will be used with. length has to be a positive value. - /// - /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values. - /// we don't validate either the returned offset is greater than the input length. - /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and - /// then used to index a collection will get out of range exception which will be same affect as the validation. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetOffset(int length) - { - int offset = _value; - if (IsFromEnd) - { - // offset = length - (~value) - // offset = length + (~(~value) + 1) - // offset = length + value + 1 - offset += length + 1; - } - - return offset; - } - - /// Indicates whether the current Index object is equal to another object of the same type. - /// An object to compare with this object. - public override bool Equals(object? obj) => obj is Index index && _value == index._value; - - /// Indicates whether the current Index object is equal to another Index object. - /// An object to compare with this object. - public bool Equals(Index other) => _value == other._value; - - /// Returns the hash code for this instance. - public override int GetHashCode() => _value; - - /// Converts integer number to an Index. - [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Matches implementation from dotnet/runtime")] - public static implicit operator Index(int value) => FromStart(value); - - /// Converts the value of the current Index object to its equivalent string representation. - [SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "Matches implementation from dotnet/runtime")] - public override string ToString() - => IsFromEnd - ? '^' + Value.ToString() - : ((uint)Value).ToString(); - } - - /// Represent a range has start and end indexes. - /// - /// Range is used by the C# compiler to support the range syntax. - /// - /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; - /// int[] subArray1 = someArray[0..2]; // { 1, 2 } - /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } - /// - /// - [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Matches implementation from dotnet/runtime")] - [SuppressMessage("Usage", "CA2231:Overload operator equals on overriding value type Equals", Justification = "Matches implementation from dotnet/runtime")] - internal readonly struct Range : IEquatable - { - /// Gets represent the inclusive start index of the Range. - public Index Start { get; } - - /// Gets represent the exclusive end index of the Range. - public Index End { get; } - - /// Initializes a new instance of the struct.Construct a Range object using the start and end indexes. - /// Represent the inclusive start index of the range. - /// Represent the exclusive end index of the range. - public Range(Index start, Index end) - { - Start = start; - End = end; - } - - /// Indicates whether the current Range object is equal to another object of the same type. - /// An object to compare with this object. - public override bool Equals(object? obj) => - obj is Range r && - r.Start.Equals(Start) && - r.End.Equals(End); - - /// Indicates whether the current Range object is equal to another Range object. - /// An object to compare with this object. - public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End); - - /// Returns the hash code for this instance. - public override int GetHashCode() - => RoslynHashCode.Combine(Start.GetHashCode(), End.GetHashCode()); - - /// Converts the value of the current Range object to its equivalent string representation. - public override string ToString() - => Start.ToString() + ".." + End.ToString(); - - /// Create a Range object starting from start index to the end of the collection. - public static Range StartAt(Index start) => new(start, Index.End); - - /// Create a Range object starting from first element in the collection to the end Index. - public static Range EndAt(Index end) => new(Index.Start, end); - - /// Gets create a Range object starting from first element to the end. - public static Range All => new(Index.Start, Index.End); - - /// Calculate the start offset and length of range object using a collection length. - /// The length of the collection that the range will be used with. length has to be a positive value. - /// - /// For performance reason, we don't validate the input length parameter against negative values. - /// It is expected Range will be used with collections which always have non negative length/count. - /// We validate the range is inside the length scope though. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public (int Offset, int Length) GetOffsetAndLength(int length) - { - int start; - Index startIndex = Start; - start = startIndex.IsFromEnd - ? length - startIndex.Value - : startIndex.Value; - - int end; - Index endIndex = End; - end = endIndex.IsFromEnd - ? length - endIndex.Value - : endIndex.Value; - - return (uint)end > (uint)length || (uint)start > (uint)end - ? throw new ArgumentOutOfRangeException(nameof(length)) - : ((int Offset, int Length))(start, end - start); - } - } -#endif -} - -// This was copied from https://github.com/dotnet/coreclr/blob/60f1e6265bd1039f023a82e0643b524d6aaf7845/src/System.Private.CoreLib/shared/System/Diagnostics/CodeAnalysis/NullableAttributes.cs -// and updated to have the scope of the attributes be internal. -namespace System.Diagnostics.CodeAnalysis -{ - /// Specifies that null is allowed as an input even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class AllowNullAttribute : Attribute { } -#pragma warning restore SA1502 // Element should not be on a single line - - /// Specifies that null is disallowed as an input even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class DisallowNullAttribute : Attribute - { - } - - /// Specifies that an output may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class MaybeNullAttribute : Attribute - { - } - - /// Specifies that an output will not be null even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class NotNullAttribute : Attribute - { - } - - /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class MaybeNullWhenAttribute : Attribute - { - /// Initializes a new instance of the class.Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter may be null. - /// - public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets a value indicating whether gets the return value condition. - public bool ReturnValue { get; } - } - - /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class NotNullWhenAttribute : Attribute - { - /// Initializes a new instance of the class.Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets a value indicating whether gets the return value condition. - public bool ReturnValue { get; } - } - - /// Specifies that the output will be non-null if the named parameter is non-null. - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] - internal sealed class NotNullIfNotNullAttribute : Attribute - { - /// Initializes a new instance of the class.Initializes the attribute with the associated parameter name. - /// - /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. - /// - public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; - - /// Gets the associated parameter name. - public string ParameterName { get; } - } - - /// Applied to a method that will never return under any circumstance. - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - internal sealed class DoesNotReturnAttribute : Attribute - { - } - - /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class DoesNotReturnIfAttribute : Attribute - { - /// Initializes a new instance of the class.Initializes the attribute with the specified parameter value. - /// - /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to - /// the associated parameter matches this value. - /// - public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; - - /// Gets a value indicating whether gets the condition parameter value. - public bool ParameterValue { get; } - } - - /// Specifies that the method or property will ensure that the listed field and property members have not-null values. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullAttribute : Attribute - { - /// Initializes a new instance of the class.Initializes the attribute with a field or property member. - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullAttribute(string member) => Members = [member]; - - /// Initializes a new instance of the class.Initializes the attribute with the list of field and property members. - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullAttribute(params string[] members) => Members = members; - - /// Gets field or property member names. - public string[] Members { get; } - } - - /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullWhenAttribute : Attribute - { - /// Initializes a new instance of the class.Initializes the attribute with the specified return value condition and a field or property member. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, string member) - { - ReturnValue = returnValue; - Members = [member]; - } - - /// Initializes a new instance of the class.Initializes the attribute with the specified return value condition and list of field and property members. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, params string[] members) - { - ReturnValue = returnValue; - Members = members; - } - - /// Gets a value indicating whether gets the return value condition. - public bool ReturnValue { get; } - - /// Gets field or property member names. - public string[] Members { get; } - } -} - -namespace System.Text -{ - internal static class StringBuilderExtensions - { - public static StringBuilder Append(this StringBuilder builder, IFormatProvider? _, string value) - => builder.Append(value); - - public static StringBuilder AppendLine(this StringBuilder builder, IFormatProvider? _, string value) - => builder.AppendLine(value); - } -} - -#endif - -#if NETCOREAPP3_1 -namespace System.Diagnostics.CodeAnalysis -{ - /// Specifies that the method or property will ensure that the listed field and property members have not-null values. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullAttribute : Attribute - { - /// Initializes the attribute with a field or property member. - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullAttribute(string member) => Members = new[] { member }; - - /// Initializes the attribute with the list of field and property members. - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullAttribute(params string[] members) => Members = members; - - /// Gets field or property member names. - public string[] Members { get; } - } - - /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition and a field or property member. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, string member) - { - ReturnValue = returnValue; - Members = new[] { member }; - } - - /// Initializes the attribute with the specified return value condition and list of field and property members. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, params string[] members) - { - ReturnValue = returnValue; - Members = members; - } - - /// Gets the return value condition. - public bool ReturnValue { get; } - - /// Gets field or property member names. - public string[] Members { get; } - } -} -#endif - -#if !NET7_0_OR_GREATER -namespace System.Diagnostics.CodeAnalysis -{ - /// - /// Specifies that this constructor sets all required members for the current type, - /// and callers do not need to set any required members themselves. - /// - [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] - internal sealed class SetsRequiredMembersAttribute : Attribute - { - } -} - -#pragma warning disable SA1403 // File may only contain a single namespace -namespace System.Runtime.CompilerServices -{ - /// - /// Indicates that compiler support for a particular feature is required for the location where this attribute is applied. - /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] - internal sealed class CompilerFeatureRequiredAttribute : Attribute - { - /// - /// The used for the ref structs C# feature. - /// - public const string RefStructs = nameof(RefStructs); - - /// - /// The used for the required members C# feature. - /// - public const string RequiredMembers = nameof(RequiredMembers); - - /// - /// Initializes a new instance of the class. - /// - /// The name of the feature to indicate. - public CompilerFeatureRequiredAttribute(string featureName) - { - FeatureName = featureName; - } - - /// - /// Gets the name of the compiler feature. - /// - public string FeatureName { get; } - - /// - /// Gets or sets a value indicating whether if true, the compiler can choose to allow access to the location where this attribute is applied if it does not understand . - /// - public bool IsOptional { get; set; } - } - - /// - /// Specifies that a type has required members or that a member is required. - /// - [AttributeUsage( - AttributeTargets.Class | - AttributeTargets.Struct | - AttributeTargets.Field | - AttributeTargets.Property, - AllowMultiple = false, - Inherited = false)] - internal sealed class RequiredMemberAttribute : Attribute - { - } -} -#endif diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/TaskExtensions.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/TaskExtensions.cs index d4f2e2cf2d..7104a74229 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/TaskExtensions.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/TaskExtensions.cs @@ -128,7 +128,7 @@ public static async Task TimeoutAfterAsync(this Task task, TimeSpan timeout, boo CancellationTokenSource cts = new(); if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token)).ConfigureAwait(false)) { - cts.Cancel(); + await cts.CancelAsync(); await task.ConfigureAwait(false); } else @@ -178,7 +178,7 @@ public static async Task TimeoutAfterAsync(this Task task, TimeSpan timeout, Can var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, cts.Token); if (task == await Task.WhenAny(task, Task.Delay(timeout, linkedTokenSource.Token)).ConfigureAwait(false)) { - cts.Cancel(); + await cts.CancelAsync(); await task.ConfigureAwait(false); } else diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/TestApplicationBuilderExtensions.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/TestApplicationBuilderExtensions.cs index 6a8c5122ee..512dc9e6a8 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/TestApplicationBuilderExtensions.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/TestApplicationBuilderExtensions.cs @@ -1,13 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics.CodeAnalysis; + using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; namespace Microsoft.Testing.Platform.Helpers; -internal static class TestApplicationBuilderExtensions +[Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] +[SuppressMessage("ApiDesign", "RS0016:Add public types and members to the declared API", Justification = "Experimental API")] +public static class TestApplicationBuilderExtensions { public static void AddTreeNodeFilterService(this ITestApplicationBuilder testApplicationBuilder, IExtension extension) => testApplicationBuilder.CommandLine.AddProvider(() => new TreeNodeFilterCommandLineOptionsProvider(extension)); diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/TimeoutHelper.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/TimeoutHelper.cs index 036c156d01..174991fda6 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/TimeoutHelper.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/TimeoutHelper.cs @@ -34,5 +34,5 @@ static TimeoutHelper() public static int DefaultHangTimeoutSeconds { get; private set; } - public static TimeSpan DefaultHangTimeSpanTimeout { get; private set; } + public static TimeSpan DefaultHangTimeSpanTimeout { get; } } diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/CommonTestHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/CommonTestHost.cs index f6cbeaaee2..94d17f2d49 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/CommonTestHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/CommonTestHost.cs @@ -17,16 +17,16 @@ internal abstract class CommonTestHost(ServiceProvider serviceProvider) : ITestH { public ServiceProvider ServiceProvider { get; } = serviceProvider; - protected abstract bool RunTestApplicationLifecycleCallbacks { get; } + protected abstract bool RunTestApplicationLifeCycleCallbacks { get; } public async Task RunAsync() { CancellationToken testApplicationCancellationToken = ServiceProvider.GetTestApplicationCancellationTokenSource().CancellationToken; - int exitCode = ExitCodes.GenericFailure; + int exitCode; try { - if (RunTestApplicationLifecycleCallbacks) + if (RunTestApplicationLifeCycleCallbacks) { // Get the test application lifecycle callbacks to be able to call the before run foreach (ITestApplicationLifecycleCallbacks testApplicationLifecycleCallbacks in ServiceProvider.GetServicesInternal()) @@ -37,7 +37,7 @@ public async Task RunAsync() exitCode = await InternalRunAsync(); - if (RunTestApplicationLifecycleCallbacks) + if (RunTestApplicationLifeCycleCallbacks) { foreach (ITestApplicationLifecycleCallbacks testApplicationLifecycleCallbacks in ServiceProvider.GetServicesInternal()) { diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/ConsoleTestHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/ConsoleTestHost.cs index dec706f674..c0002b1242 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/ConsoleTestHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/ConsoleTestHost.cs @@ -7,11 +7,9 @@ using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions.TestFramework; -using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Messages; -using Microsoft.Testing.Platform.OutputDevice; using Microsoft.Testing.Platform.Requests; using Microsoft.Testing.Platform.Services; using Microsoft.Testing.Platform.Telemetry; @@ -21,40 +19,21 @@ namespace Microsoft.Testing.Platform.Hosts; internal sealed class ConsoleTestHost( ServiceProvider serviceProvider, - Func, - TestFrameworkManager, - TestHostManager, - MessageBusProxy, - bool, - Task> buildTestFrameworkAsync, + Func> buildTestFrameworkAsync, TestFrameworkManager testFrameworkManager, - TestHostManager testHostManager) : CommonTestHost(serviceProvider) + TestHostManager testHostManager) + : CommonTestHost(serviceProvider) { private static readonly ClientInfo Client = new("testingplatform-console", AppVersion.DefaultSemVer); private readonly ILogger _logger = serviceProvider.GetLoggerFactory().CreateLogger(); private readonly IClock _clock = serviceProvider.GetClock(); - private readonly Func, - TestFrameworkManager, - TestHostManager, - MessageBusProxy, - bool, - Task> _buildTestFrameworkAsync = buildTestFrameworkAsync; + private readonly Func> _buildTestFrameworkAsync = buildTestFrameworkAsync; private readonly TestFrameworkManager _testFrameworkManager = testFrameworkManager; private readonly TestHostManager _testHostManager = testHostManager; - protected override bool RunTestApplicationLifecycleCallbacks => true; + protected override bool RunTestApplicationLifeCycleCallbacks => true; protected override async Task InternalRunAsync() { @@ -73,24 +52,24 @@ protected override async Task InternalRunAsync() ?? new TestHostTestFrameworkInvoker(ServiceProvider); ServiceProvider.TryAddService(new Services.TestSessionContext(abortRun)); - ITestFramework testFrameworkAdapter = await _buildTestFrameworkAsync( + ITestFramework testFramework = await _buildTestFrameworkAsync(new( ServiceProvider, new ConsoleTestExecutionRequestFactory(ServiceProvider.GetCommandLineOptions(), testExecutionFilterFactory), testAdapterInvoker, testExecutionFilterFactory, ServiceProvider.GetPlatformOutputDevice(), - Enumerable.Empty(), + [], _testFrameworkManager, _testHostManager, new MessageBusProxy(), - ServiceProvider.GetCommandLineOptions().IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey)); + ServiceProvider.GetCommandLineOptions().IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey))); ITelemetryCollector telemetry = ServiceProvider.GetTelemetryCollector(); ITelemetryInformation telemetryInformation = ServiceProvider.GetTelemetryInformation(); Statistics? statistics = null; string? extensionInformation = null; await _logger.LogInformationAsync($"Starting test session '{ServiceProvider.GetTestSessionContext().SessionId}'"); - int exitCode = ExitCodes.GenericFailure; + int exitCode; DateTimeOffset adapterLoadStop = _clock.UtcNow; DateTimeOffset requestExecuteStart = _clock.UtcNow; DateTimeOffset? requestExecuteStop = null; @@ -103,7 +82,7 @@ await ExecuteRequestAsync( testSessionInfo, ServiceProvider, ServiceProvider.GetBaseMessageBus(), - testFrameworkAdapter, + testFramework, Client); requestExecuteStop = _clock.UtcNow; @@ -122,10 +101,7 @@ await ExecuteRequestAsync( } catch (OperationCanceledException oc) when (oc.CancellationToken == abortRun) { - if (requestExecuteStop == null) - { - requestExecuteStop = _clock.UtcNow; - } + requestExecuteStop ??= _clock.UtcNow; exitCode = ExitCodes.TestSessionAborted; await _logger.LogInformationAsync("Test session cancelled."); diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/ITestHostBuilder.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/ITestHostBuilder.cs index 81b7ef6e8e..74aeee21e0 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/ITestHostBuilder.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/ITestHostBuilder.cs @@ -41,5 +41,5 @@ internal interface ITestHostBuilder IToolsManager Tools { get; } - Task BuildAsync(string[] args, ApplicationLoggingState loggingState, TestApplicationOptions testApplicationOptions, IUnhandledExceptionsHandler unhandledExceptionsHandler, DateTimeOffset createBuilderStart); + Task BuildAsync(ApplicationLoggingState loggingState, TestApplicationOptions testApplicationOptions, IUnhandledExceptionsHandler unhandledExceptionsHandler, DateTimeOffset createBuilderStart); } diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/ServerTestHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/ServerTestHost.cs index d067d04b32..481d371c3d 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/ServerTestHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/ServerTestHost.cs @@ -9,11 +9,9 @@ using Microsoft.Testing.Platform.Capabilities.TestFramework; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestFramework; -using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Messages; -using Microsoft.Testing.Platform.OutputDevice; using Microsoft.Testing.Platform.Requests; using Microsoft.Testing.Platform.ServerMode; using Microsoft.Testing.Platform.Services; @@ -25,19 +23,7 @@ namespace Microsoft.Testing.Platform.Hosts; internal sealed partial class ServerTestHost : CommonTestHost, IServerTestHost, IDisposable { private const string ProtocolVersion = "1.0.0"; - private readonly Func< - ServiceProvider, - ITestExecutionRequestFactory, - ITestFrameworkInvoker, - ITestExecutionFilterFactory, - IPlatformOutputDevice, - IEnumerable, - TestFrameworkManager, - TestHostManager, - MessageBusProxy, - bool, - Task> - _buildTestFrameworkAsync; + private readonly Func> _buildTestFrameworkAsync; private readonly IMessageHandlerFactory _messageHandlerFactory; private readonly TestFrameworkManager _testFrameworkManager; @@ -70,17 +56,7 @@ private readonly Func< public ServerTestHost( ServiceProvider serviceProvider, - Func, - TestFrameworkManager, - TestHostManager, - MessageBusProxy, - bool, - Task> buildTestFrameworkAsync, + Func> buildTestFrameworkAsync, IMessageHandlerFactory messageHandlerFactory, TestFrameworkManager testFrameworkManager, TestHostManager testSessionManager) @@ -114,7 +90,7 @@ public ServerTestHost( public bool IsInitialized => _messageHandler is not null; - protected override bool RunTestApplicationLifecycleCallbacks => true; + protected override bool RunTestApplicationLifeCycleCallbacks => true; private void OnCurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) => _logger.LogError($"[ServerTestHost.OnCurrentDomainUnhandledException] {e.ExceptionObject}{_environment.NewLine}IsTerminating: {e.IsTerminating}"); @@ -138,7 +114,7 @@ protected override async Task InternalRunAsync() { try { - await _logger.LogInformationAsync("Starting server mode"); + await _logger.LogDebugAsync("Starting server mode"); _messageHandler = await _messageHandlerFactory.CreateMessageHandlerAsync(_testApplicationCancellationTokenSource.CancellationToken); // Initialize the ServerLoggerForwarderProvider, it can be null if diagnostic is disabled. @@ -203,12 +179,8 @@ private async Task HandleMessagesAsync() // Signal only one time if (!_serverClosingTokenSource.IsCancellationRequested) { - await _logger.LogInformationAsync("Server requested to shutdown"); -#if NET8_0_OR_GREATER + await _logger.LogDebugAsync("Server requested to shutdown"); await _serverClosingTokenSource.CancelAsync(); -#else - _serverClosingTokenSource.Cancel(); -#endif } // Signal the exit call @@ -217,11 +189,7 @@ private async Task HandleMessagesAsync() // If there're no in-flight request we can close the server if (_clientToServerRequests.IsEmpty) { -#if NET8_0_OR_GREATER await _stopMessageHandler.CancelAsync(); -#else - _stopMessageHandler.Cancel(); -#endif } continue; @@ -403,32 +371,24 @@ private async Task HandleRequestCoreAsync(RequestMessage message, RpcInv AssertInitialized(); - await _logger.LogInformationAsync($"Received {message.Method} request"); + await _logger.LogDebugAsync($"Received {message.Method} request"); switch (message.Method, message.Params) { case (JsonRpcMethods.Initialize, InitializeRequestArgs args): { _client = new(args.ClientInfo.Name, args.ClientInfo.Version); - await _logger.LogInformationAsync($"Connection established with '{_client.Id}', protocol version {_client.Version}"); - - // Get the capabilities of the test framework - ITestFrameworkCapabilities capabilities = _testFrameworkManager.TestFrameworkCapabilitiesFactory(ServiceProvider); - try - { - INamedFeatureCapability? namedFeatureCapability = capabilities.GetCapability(); - return new InitializeResponseArgs( - ServerInfo: new ServerInfo("test-anywhere", Version: ProtocolVersion), - Capabilities: new ServerCapabilities( - new ServerTestingCapabilities( - SupportsDiscovery: true, - MultiRequestSupport: namedFeatureCapability?.IsSupported(JsonRpcStrings.MultiRequestSupport) == true, - VSTestProviderSupport: namedFeatureCapability?.IsSupported(JsonRpcStrings.VSTestProviderSupport) == true))); - } - finally - { - await DisposeHelper.DisposeAsync(capabilities); - } + await _logger.LogDebugAsync($"Connection established with '{_client.Id}', protocol version {_client.Version}"); + + INamedFeatureCapability? namedFeatureCapability = ServiceProvider.GetTestFrameworkCapabilities().GetCapability(); + return new InitializeResponseArgs( + ProcessId: ServiceProvider.GetProcessHandler().GetCurrentProcess().Id, + ServerInfo: new ServerInfo("test-anywhere", Version: ProtocolVersion), + Capabilities: new ServerCapabilities( + new ServerTestingCapabilities( + SupportsDiscovery: true, + MultiRequestSupport: namedFeatureCapability?.IsSupported(JsonRpcStrings.MultiRequestSupport) == true, + VSTestProviderSupport: namedFeatureCapability?.IsSupported(JsonRpcStrings.VSTestProviderSupport) == true))); } case (JsonRpcMethods.TestingDiscoverTests, DiscoverRequestArgs args): @@ -482,17 +442,17 @@ private async Task ExecuteRequestAsync(RequestArgsBase args, s DateTimeOffset adapterLoadStart = _clock.UtcNow; // Build the per request adapter - ITestFramework perRequestTestFramework = await _buildTestFrameworkAsync( - perRequestServiceProvider, - requestFactory, - invoker, - filterFactory, - new ServerModePerCallOutputDevice(), - new IDataConsumer[] { testNodeUpdateProcessor }, - _testFrameworkManager, - _testSessionManager, - new MessageBusProxy(), - method == JsonRpcMethods.TestingDiscoverTests); + ITestFramework perRequestTestFramework = await _buildTestFrameworkAsync(new( + perRequestServiceProvider, + requestFactory, + invoker, + filterFactory, + new ServerModePerCallOutputDevice(), + [testNodeUpdateProcessor], + _testFrameworkManager, + _testSessionManager, + new MessageBusProxy(), + method == JsonRpcMethods.TestingDiscoverTests)); DateTimeOffset adapterLoadStop = _clock.UtcNow; @@ -527,10 +487,7 @@ await ExecuteRequestAsync( } finally { - if (requestExecuteStop == null) - { - requestExecuteStop = _clock.UtcNow; - } + requestExecuteStop ??= _clock.UtcNow; // Cleanup all services // We skip all services that are "cloned" per call because are reused and will be disposed on shutdown. @@ -551,14 +508,14 @@ await ExecuteRequestAsync( (RunRequestArgs)args, requestStart, requestStop, adapterLoadStart, adapterLoadStop, - requestExecuteStart, (DateTimeOffset)requestExecuteStop!, + requestExecuteStart, (DateTimeOffset)requestExecuteStop, testNodeUpdateProcessor.GetTestNodeStatistics()) : method == JsonRpcMethods.TestingDiscoverTests ? GetDiscoveryMetrics( (DiscoverRequestArgs)args, requestStart, requestStop, adapterLoadStart, adapterLoadStop, - requestExecuteStart, (DateTimeOffset)requestExecuteStop!, + requestExecuteStart, (DateTimeOffset)requestExecuteStop, testNodeUpdateProcessor.GetTestNodeStatistics().TotalDiscoveredTests) : throw new NotImplementedException($"Request not implemented '{method}'"); @@ -585,7 +542,7 @@ internal static Dictionary GetDiscoveryMetrics( { TelemetryProperties.RequestProperties.AdapterLoadStop, adapterLoadStop }, { TelemetryProperties.RequestProperties.RequestExecuteStart, requestExecuteStart }, { TelemetryProperties.RequestProperties.RequestExecuteStop, requestExecuteStop }, - { TelemetryProperties.RequestProperties.IsFilterEnabledPropertyName, (args?.TestNodes is not null || args?.GraphFilter is not null).AsTelemetryBool() }, + { TelemetryProperties.RequestProperties.IsFilterEnabledPropertyName, (args.TestNodes is not null || args?.GraphFilter is not null).AsTelemetryBool() }, }; internal static Dictionary GetRunMetrics( @@ -605,7 +562,7 @@ internal static Dictionary GetRunMetrics( { TelemetryProperties.RequestProperties.AdapterLoadStop, adapterLoadStop }, { TelemetryProperties.RequestProperties.RequestExecuteStart, requestExecuteStart }, { TelemetryProperties.RequestProperties.RequestExecuteStop, requestExecuteStop }, - { TelemetryProperties.RequestProperties.IsFilterEnabledPropertyName, (args?.TestNodes is not null || args?.GraphFilter is not null).AsTelemetryBool() }, + { TelemetryProperties.RequestProperties.IsFilterEnabledPropertyName, (args.TestNodes is not null || args?.GraphFilter is not null).AsTelemetryBool() }, }; private async Task SendErrorAsync(int reqId, int errorCode, string message, object? data, CancellationToken cancellationToken) diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/TestFrameworkBuilderData.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/TestFrameworkBuilderData.cs new file mode 100644 index 0000000000..b5c5b23241 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/TestFrameworkBuilderData.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Internal.Framework; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.Services; +using Microsoft.Testing.Platform.TestHost; + +namespace Microsoft.Testing.Platform.Hosts; + +internal class TestFrameworkBuilderData(ServiceProvider serviceProvider, ITestExecutionRequestFactory testExecutionRequestFactory, + ITestFrameworkInvoker testExecutionRequestInvoker, ITestExecutionFilterFactory testExecutionFilterFactory, + IPlatformOutputDevice platformOutputDisplayService, IEnumerable serverPerCallConsumers, + TestFrameworkManager testFrameworkManager, TestHostManager testSessionManager, MessageBusProxy messageBusProxy, + bool isForDiscoveryRequest) +{ + public ServiceProvider ServiceProvider { get; } = serviceProvider; + + public ITestExecutionRequestFactory TestExecutionRequestFactory { get; } = testExecutionRequestFactory; + + public ITestFrameworkInvoker TestExecutionRequestInvoker { get; } = testExecutionRequestInvoker; + + public ITestExecutionFilterFactory TestExecutionFilterFactory { get; } = testExecutionFilterFactory; + + public IPlatformOutputDevice PlatformOutputDisplayService { get; } = platformOutputDisplayService; + + public IEnumerable ServerPerCallConsumers { get; } = serverPerCallConsumers; + + public TestFrameworkManager TestFrameworkManager { get; } = testFrameworkManager; + + public TestHostManager TestSessionManager { get; } = testSessionManager; + + public MessageBusProxy MessageBusProxy { get; } = messageBusProxy; + + public bool IsForDiscoveryRequest { get; } = isForDiscoveryRequest; +} diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs index f4fc07c669..9a8bac55d5 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs @@ -12,6 +12,7 @@ using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; using Microsoft.Testing.Platform.Extensions.TestFramework; using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Extensions.TestHostOrchestrator; @@ -34,6 +35,8 @@ namespace Microsoft.Testing.Platform.Hosts; internal class TestHostBuilder(IFileSystem fileSystem, IRuntimeFeature runtimeFeature, IEnvironment environment, IProcessHandler processHandler, ITestApplicationModuleInfo testApplicationModuleInfo) : ITestHostBuilder { + private const string ServerOptionValue = "dotnettestcli"; + private readonly IFileSystem _fileSystem = fileSystem; private readonly ITestApplicationModuleInfo _testApplicationModuleInfo = testApplicationModuleInfo; @@ -60,7 +63,6 @@ internal class TestHostBuilder(IFileSystem fileSystem, IRuntimeFeature runtimeFe public ITestHostOrchestratorManager TestHostOrchestratorManager { get; } = new TestHostOrchestratorManager(); public async Task BuildAsync( - string[] args, ApplicationLoggingState loggingState, TestApplicationOptions testApplicationOptions, IUnhandledExceptionsHandler unhandledExceptionsHandler, @@ -110,6 +112,9 @@ public async Task BuildAsync( SystemMonitorAsyncFactory systemMonitorAsyncFactory = new(); serviceProvider.TryAddService(systemMonitorAsyncFactory); + PlatformInformation platformInformation = new(); + serviceProvider.AddService(platformInformation); + ILogger? logger = null; if (loggingState.FileLoggerProvider is not null) { @@ -148,7 +153,7 @@ public async Task BuildAsync( // Check the environment variable, it wins on all the other configuration. string? environmentSetting = environment.GetEnvironmentVariable(EnvironmentVariableConstants.TESTINGPLATFORM_EXIT_PROCESS_ON_UNHANDLED_EXCEPTION); bool? isEnvConfiguredToFailFast = null; - if (environmentSetting is not null and ("1" or "0")) + if (environmentSetting is "1" or "0") { isEnvConfiguredToFailFast = environmentSetting == "1"; } @@ -192,15 +197,29 @@ public async Task BuildAsync( // Build the command line service - we need special treatment because is possible that an extension query it during the creation. // Add Retry default argument commandlines - CommandLineHandler commandLineHandler = await ((CommandLineManager)CommandLine).BuildAsync(args, platformOutputDevice, loggingState.CommandLineParseResult); + CommandLineHandler commandLineHandler = await ((CommandLineManager)CommandLine).BuildAsync(platformOutputDevice, loggingState.CommandLineParseResult); + + // Create the test framework capabilities + ITestFrameworkCapabilities testFrameworkCapabilities = TestFramework.TestFrameworkCapabilitiesFactory(serviceProvider); + if (testFrameworkCapabilities is IAsyncInitializableExtension testFrameworkCapabilitiesAsyncInitializable) + { + await testFrameworkCapabilitiesAsyncInitializable.InitializeAsync(); + } + + // Register the test framework capabilities to be used by services + serviceProvider.AddService(testFrameworkCapabilities); // If command line is not valid we return immediately. - (bool parseSucceeded, string? validationError) = await commandLineHandler.TryParseAndValidateAsync(); - if (!loggingState.CommandLineParseResult.HasTool && !parseSucceeded) + ValidationResult commandLineValidationResult = await CommandLineOptionsValidator.ValidateAsync( + loggingState.CommandLineParseResult, + commandLineHandler.SystemCommandLineOptionsProviders, + commandLineHandler.ExtensionsCommandLineOptionsProviders, + commandLineHandler); + + if (!loggingState.CommandLineParseResult.HasTool && !commandLineValidationResult.IsValid) { - await DisplayBannerIfEnabledAsync(loggingState, platformOutputDevice); - ArgumentGuard.IsNotNull(validationError); - await platformOutputDevice.DisplayAsync(commandLineHandler, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(validationError)); + await DisplayBannerIfEnabledAsync(loggingState, platformOutputDevice, testFrameworkCapabilities); + await platformOutputDevice.DisplayAsync(commandLineHandler, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(commandLineValidationResult.ErrorMessage)); await commandLineHandler.PrintHelpAsync(); return new InformativeCommandLineTestHost(ExitCodes.InvalidCommandLine); } @@ -235,7 +254,7 @@ public async Task BuildAsync( // Display banner now because we need capture the output in case of MSBuild integration and we want to forward // to file disc also the banner, so at this point we need to have all services and configuration(result directory) built. - await DisplayBannerIfEnabledAsync(loggingState, platformOutputDevice); + await DisplayBannerIfEnabledAsync(loggingState, platformOutputDevice, testFrameworkCapabilities); // Add global telemetry service. // Add at this point or the telemetry banner appearance order will be wrong, we want the testing app banner before the telemetry banner. @@ -266,13 +285,7 @@ public async Task BuildAsync( // Add the platform output device to the service provider. serviceProvider.TryAddService(platformOutputDevice); - ToolsTestHost toolsTestHost = new( - toolsInformation, - serviceProvider, - loggingState.CommandLineParseResult, - commandLineHandler.ExtensionsCommandLineOptionsProviders, - commandLineHandler, - platformOutputDevice); + ToolsTestHost toolsTestHost = new(toolsInformation, serviceProvider, commandLineHandler, platformOutputDevice); await LogTestHostCreatedAsync( serviceProvider, @@ -283,18 +296,28 @@ await LogTestHostCreatedAsync( return toolsTestHost; } + NamedPipeClient? dotnetTestPipeClient = await ConnectToDotnetTestPipeIfAvailableAsync(commandLineHandler, testApplicationCancellationTokenSource); + // If --help is invoked we return if (commandLineHandler.IsHelpInvoked()) { - await commandLineHandler.PrintHelpAsync(toolsInformation.Tools); - return new InformativeCommandLineTestHost(0); + if (dotnetTestPipeClient is not null) + { + await SendCommandLineOptionsToDotnetTestPipeAsync(dotnetTestPipeClient, commandLineHandler, testApplicationCancellationTokenSource.CancellationToken); + } + else + { + await commandLineHandler.PrintHelpAsync(toolsInformation.Tools); + } + + return new InformativeCommandLineTestHost(0, dotnetTestPipeClient); } // If --info is invoked we return if (commandLineHandler.IsInfoInvoked()) { await commandLineHandler.PrintInfoAsync(toolsInformation.Tools); - return new InformativeCommandLineTestHost(0); + return new InformativeCommandLineTestHost(0, dotnetTestPipeClient); } // ======= TEST HOST ORCHESTRATOR ======== // @@ -432,6 +455,44 @@ await LogTestHostCreatedAsync( } } + private async Task SendCommandLineOptionsToDotnetTestPipeAsync(NamedPipeClient namedPipeClient, CommandLineHandler commandLineHandler, CancellationToken cancellationToken) + { + List commandLineHelpOptions = new(); + foreach (ICommandLineOptionsProvider commandLineOptionProvider in commandLineHandler.CommandLineOptionsProviders) + { + foreach (CommandLineOption commandLineOption in commandLineOptionProvider.GetCommandLineOptions()) + { + commandLineHelpOptions.Add(new CommandLineOptionMessage( + commandLineOption.Name, + commandLineOption.Description, + commandLineOption.IsHidden, + commandLineOption.IsBuiltIn)); + } + } + + await namedPipeClient.RequestReplyAsync(new CommandLineOptionMessages(_testApplicationModuleInfo.GetCurrentTestApplicationFullPath(), commandLineHelpOptions.OrderBy(option => option.Name).ToArray()), cancellationToken); + } + + private static async Task ConnectToDotnetTestPipeIfAvailableAsync(CommandLineHandler commandLineHandler, CTRLPlusCCancellationTokenSource cancellationTokenSource) + { + NamedPipeClient? namedPipeClient = null; + + // If we are in server mode and the pipe name is provided + // then, we need to connect to the pipe server. + if (commandLineHandler.TryGetOptionArgumentList(PlatformCommandLineProvider.ServerOptionKey, out string[]? serverArgs) && + serverArgs.Length == 1 && + serverArgs[0].Equals(ServerOptionValue, StringComparison.Ordinal) && + commandLineHandler.TryGetOptionArgumentList(PlatformCommandLineProvider.DotNetTestPipeOptionKey, out string[]? arguments)) + { + namedPipeClient = new(arguments[0]); + namedPipeClient.RegisterAllSerializers(); + + await namedPipeClient.ConnectAsync(cancellationTokenSource.CancellationToken); + } + + return namedPipeClient; + } + private static async Task ConnectToTestHostProcessMonitorIfAvailableAsync( IProcessHandler processHandler, CTRLPlusCCancellationTokenSource testApplicationCancellationTokenSource, @@ -467,8 +528,9 @@ await LogTestHostCreatedAsync( await logger.LogDebugAsync($"Connected to named pipe '{pipeName}'"); // Send the PID + using IProcess currentProcess = processHandler.GetCurrentProcess(); await client.RequestReplyAsync( - new TestHostProcessPIDRequest(processHandler.GetCurrentProcess().Id), + new TestHostProcessPIDRequest(currentProcess.Id), testApplicationCancellationTokenSource.CancellationToken); return client; } @@ -531,49 +593,30 @@ private static async Task LogTestHostCreatedAsync( } } - private async Task BuildTestFrameworkAsync( - ServiceProvider serviceProvider, - ITestExecutionRequestFactory testExecutionRequestFactory, ITestFrameworkInvoker testExecutionRequestInvoker, - ITestExecutionFilterFactory testExecutionFilterFactory, IPlatformOutputDevice platformOutputDisplayService, - IEnumerable serverPerCallConsumers, - TestFrameworkManager testFrameworkManager, - TestHostManager testSessionManager, - MessageBusProxy guardMessageBusService, - bool isForDiscoveryRequest) + private async Task BuildTestFrameworkAsync(TestFrameworkBuilderData testFrameworkBuilderData) { // Add the message bus proxy - serviceProvider.AddService(guardMessageBusService); - - // Create the test framework capabilities - ITestFrameworkCapabilities testFrameworkCapabilities = testFrameworkManager.TestFrameworkCapabilitiesFactory(serviceProvider); - if (testFrameworkCapabilities is IAsyncInitializableExtension testFrameworkCapabilitiesAsyncInitializable) - { - await testFrameworkCapabilitiesAsyncInitializable.InitializeAsync(); - } - - // Register the test framework capabilities to be used by services - serviceProvider.AddService(testFrameworkCapabilities); + ServiceProvider serviceProvider = testFrameworkBuilderData.ServiceProvider; + serviceProvider.AddService(testFrameworkBuilderData.MessageBusProxy); // Build and register "common non special" services - we need special treatment because extensions can start to log during the // creations and we could lose interesting diagnostic information. List dataConsumersBuilder = []; - await RegisterAsServiceOrConsumerOrBothAsync(platformOutputDisplayService, serviceProvider, dataConsumersBuilder); - await RegisterAsServiceOrConsumerOrBothAsync(testExecutionRequestFactory, serviceProvider, dataConsumersBuilder); - await RegisterAsServiceOrConsumerOrBothAsync(testExecutionRequestInvoker, serviceProvider, dataConsumersBuilder); - await RegisterAsServiceOrConsumerOrBothAsync(testExecutionFilterFactory, serviceProvider, dataConsumersBuilder); + await RegisterAsServiceOrConsumerOrBothAsync(testFrameworkBuilderData.PlatformOutputDisplayService, serviceProvider, dataConsumersBuilder); + await RegisterAsServiceOrConsumerOrBothAsync(testFrameworkBuilderData.TestExecutionRequestFactory, serviceProvider, dataConsumersBuilder); + await RegisterAsServiceOrConsumerOrBothAsync(testFrameworkBuilderData.TestExecutionRequestInvoker, serviceProvider, dataConsumersBuilder); + await RegisterAsServiceOrConsumerOrBothAsync(testFrameworkBuilderData.TestExecutionFilterFactory, serviceProvider, dataConsumersBuilder); // Create the test framework adapter - ITestFramework testFrameworkAdapter = testFrameworkManager.TestFrameworkAdapterFactory(testFrameworkCapabilities, serviceProvider); - if (testFrameworkAdapter is IAsyncInitializableExtension testFrameworkAsyncInitializable) - { - await testFrameworkAsyncInitializable.InitializeAsync(); - } + ITestFrameworkCapabilities testFrameworkCapabilities = serviceProvider.GetTestFrameworkCapabilities(); + ITestFramework testFramework = testFrameworkBuilderData.TestFrameworkManager.TestFrameworkFactory(testFrameworkCapabilities, serviceProvider); + await testFramework.TryInitializeAsync(); serviceProvider.AllowTestAdapterFrameworkRegistration = true; try { - await RegisterAsServiceOrConsumerOrBothAsync(new TestFrameworkProxy(testFrameworkAdapter), serviceProvider, dataConsumersBuilder); + await RegisterAsServiceOrConsumerOrBothAsync(new TestFrameworkProxy(testFramework), serviceProvider, dataConsumersBuilder); } finally { @@ -587,12 +630,12 @@ private async Task BuildTestFrameworkAsync( List testSessionLifetimeHandlers = []; // When we run a discovery request we don't want to run any user extensions, the only needed extension is the test adapter that will "discover" tests. - if (!isForDiscoveryRequest) + if (!testFrameworkBuilderData.IsForDiscoveryRequest) { // We keep the bag of the already created composite service factory to reuse the instance. List newBuiltCompositeServices = []; - (IExtension Consumer, int RegistrationOrder)[] consumers = await testSessionManager.BuildDataConsumersAsync(serviceProvider, newBuiltCompositeServices); - (IExtension TestSessionLifetimeHandler, int RegistrationOrder)[] sessionLifeTimeHandlers = await testSessionManager.BuildTestSessionLifetimeHandleAsync(serviceProvider, newBuiltCompositeServices); + (IExtension Consumer, int RegistrationOrder)[] consumers = await testFrameworkBuilderData.TestSessionManager.BuildDataConsumersAsync(serviceProvider, newBuiltCompositeServices); + (IExtension TestSessionLifetimeHandler, int RegistrationOrder)[] sessionLifeTimeHandlers = await testFrameworkBuilderData.TestSessionManager.BuildTestSessionLifetimeHandleAsync(serviceProvider, newBuiltCompositeServices); // Register the test session lifetime handlers for the notifications testSessionLifetimeHandlers.AddRange(sessionLifeTimeHandlers.OrderBy(x => x.RegistrationOrder).Select(x => (ITestSessionLifetimeHandler)x.TestSessionLifetimeHandler)); @@ -613,7 +656,7 @@ private async Task BuildTestFrameworkAsync( // In server mode where we don't shutdown the process at the end of the run we need a way to use a "per-call" consumer to be able to link the // node updates to the correct request id. - foreach (IDataConsumer consumerService in serverPerCallConsumers) + foreach (IDataConsumer consumerService in testFrameworkBuilderData.ServerPerCallConsumers) { if (consumerService is ITestSessionLifetimeHandler handler) { @@ -629,7 +672,7 @@ private async Task BuildTestFrameworkAsync( // Register the ITestApplicationResult TestApplicationResult testApplicationResult = new( - platformOutputDisplayService, + testFrameworkBuilderData.PlatformOutputDisplayService, serviceProvider.GetTestApplicationCancellationTokenSource(), serviceProvider.GetCommandLineOptions(), serviceProvider.GetEnvironment()); @@ -651,7 +694,7 @@ private async Task BuildTestFrameworkAsync( serviceProvider.GetEnvironment(), serviceProvider.GetTestApplicationProcessExitCode()); await concreteMessageBusService.InitAsync(); - guardMessageBusService.SetBuiltMessageBus(concreteMessageBusService); + testFrameworkBuilderData.MessageBusProxy.SetBuiltMessageBus(concreteMessageBusService); } else { @@ -662,23 +705,15 @@ private async Task BuildTestFrameworkAsync( serviceProvider.GetLoggerFactory(), serviceProvider.GetEnvironment()); await concreteMessageBusService.InitAsync(); - guardMessageBusService.SetBuiltMessageBus(concreteMessageBusService); + testFrameworkBuilderData.MessageBusProxy.SetBuiltMessageBus(concreteMessageBusService); } - return testFrameworkAdapter; + return testFramework; } protected virtual ConsoleTestHost CreateConsoleTestHost( ServiceProvider serviceProvider, - Func, - TestFrameworkManager, - TestHostManager, - MessageBusProxy, - bool, - Task> buildTestFrameworkAsync, + Func> buildTestFrameworkAsync, TestFrameworkManager testFrameworkManager, TestHostManager testHostManager) => new(serviceProvider, buildTestFrameworkAsync, testFrameworkManager, testHostManager); @@ -728,14 +763,19 @@ private async Task RegisterAsServiceOrConsumerOrBothAsync(object service, Servic await AddServiceIfNotSkippedAsync(service, serviceProvider); } - private async Task DisplayBannerIfEnabledAsync(ApplicationLoggingState loggingState, IPlatformOutputDevice platformOutputDevice) + private async Task DisplayBannerIfEnabledAsync(ApplicationLoggingState loggingState, IPlatformOutputDevice platformOutputDevice, + ITestFrameworkCapabilities testFrameworkCapabilities) { bool isNoBannerSet = loggingState.CommandLineParseResult.IsOptionSet(PlatformCommandLineProvider.NoBannerOptionKey); string? noBannerEnvironmentVar = environment.GetEnvironmentVariable(EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER); string? dotnetNoLogoEnvironmentVar = environment.GetEnvironmentVariable(EnvironmentVariableConstants.DOTNET_NOLOGO); if (!isNoBannerSet && !(noBannerEnvironmentVar is "1" or "true") && !(dotnetNoLogoEnvironmentVar is "1" or "true")) { - await platformOutputDevice.DisplayBannerAsync(); + IBannerMessageOwnerCapability? bannerMessageOwnerCapability = testFrameworkCapabilities.GetCapability(); + string? bannerMessage = bannerMessageOwnerCapability is not null + ? await bannerMessageOwnerCapability.GetBannerMessageAsync() + : null; + await platformOutputDevice.DisplayBannerAsync(bannerMessage); } } } diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControlledHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControlledHost.cs index bfaf75bda3..1e702cb433 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControlledHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControlledHost.cs @@ -7,14 +7,14 @@ namespace Microsoft.Testing.Platform.Hosts; -internal class TestHostControlledHost(NamedPipeClient namedPipeClient, ITestHost innerTestHost, CancellationToken cancellationToken) : ITestHost, IDisposable +internal class TestHostControlledHost(NamedPipeClient testHostControllerPipeClient, ITestHost innerTestHost, CancellationToken cancellationToken) : ITestHost, IDisposable #if NETCOREAPP #pragma warning disable SA1001 // Commas should be spaced correctly , IAsyncDisposable #pragma warning restore SA1001 // Commas should be spaced correctly #endif { - private readonly NamedPipeClient _namedPipeClient = namedPipeClient; + private readonly NamedPipeClient _namedPipeClient = testHostControllerPipeClient; private readonly ITestHost _innerTestHost = innerTestHost; private readonly CancellationToken _cancellationToken = cancellationToken; diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControllersTestHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControllersTestHost.cs index e8b2cc8e53..7efc4a78c9 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControllersTestHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControllersTestHost.cs @@ -57,22 +57,20 @@ public TestHostControllersTestHost(TestHostControllerConfiguration testHostsInfo public string Description => string.Empty; - protected override bool RunTestApplicationLifecycleCallbacks => false; + protected override bool RunTestApplicationLifeCycleCallbacks => false; public Task IsEnabledAsync() => Task.FromResult(false); protected override async Task InternalRunAsync() { - int exitCode = ExitCodes.Success; + int exitCode; CancellationToken abortRun = ServiceProvider.GetTestApplicationCancellationTokenSource().CancellationToken; DateTimeOffset consoleRunStart = _clock.UtcNow; var consoleRunStarted = Stopwatch.StartNew(); IEnvironment environment = ServiceProvider.GetEnvironment(); IProcessHandler process = ServiceProvider.GetProcessHandler(); - int currentPID = process.GetCurrentProcess().Id; ITestApplicationModuleInfo testApplicationModuleInfo = ServiceProvider.GetTestApplicationModuleInfo(); ExecutableInfo executableInfo = testApplicationModuleInfo.GetCurrentExecutableInfo(); - string testApplicationFullPath = testApplicationModuleInfo.GetCurrentTestApplicationFullPath(); ITelemetryCollector telemetry = ServiceProvider.GetTelemetryCollector(); ITelemetryInformation telemetryInformation = ServiceProvider.GetTelemetryInformation(); string? extensionInformation = null; @@ -80,36 +78,19 @@ protected override async Task InternalRunAsync() IConfiguration configuration = ServiceProvider.GetConfiguration(); try { - List partialCommandLine = new(executableInfo.Arguments) - { + using IProcess currentProcess = process.GetCurrentProcess(); + int currentPID = currentProcess.Id; + string processIdString = currentPID.ToString(CultureInfo.InvariantCulture); + List partialCommandLine = + [ + .. executableInfo.Arguments, $"--{PlatformCommandLineProvider.TestHostControllerPIDOptionKey}", - process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture), - }; - CommandLineInfo finalCommandLine = new(executableInfo.FileName, partialCommandLine, testApplicationFullPath); - - ProcessStartInfo processStartInfo = new() - { - FileName = finalCommandLine.FileName, -#if !NETCOREAPP - UseShellExecute = false, -#endif - }; - - foreach (string argument in finalCommandLine.Arguments) - { -#if !NETCOREAPP - processStartInfo.Arguments += argument + " "; -#else - processStartInfo.ArgumentList.Add(argument); -#endif - } + processIdString + ]; // Prepare the environment variables used by the test host string processCorrelationId = Guid.NewGuid().ToString("N"); - processStartInfo.EnvironmentVariables.Add($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_CORRELATIONID}_{currentPID}", processCorrelationId); - processStartInfo.EnvironmentVariables.Add($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_PARENTPID}_{currentPID}", process.GetCurrentProcess()?.Id.ToString(CultureInfo.InvariantCulture) ?? "null pid"); - processStartInfo.EnvironmentVariables.Add($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_SKIPEXTENSION}_{currentPID}", "1"); - await _logger.LogDebugAsync($"{$"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_CORRELATIONID}_{currentPID}"} '{processCorrelationId}'"); + await _logger.LogDebugAsync($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_CORRELATIONID}_{currentPID} '{processCorrelationId}'"); NamedPipeServer testHostControllerIpc = new( $"MONITORTOHOST_{Guid.NewGuid():N}", @@ -118,16 +99,32 @@ protected override async Task InternalRunAsync() _loggerFactory.CreateLogger(), ServiceProvider.GetTask(), abortRun); testHostControllerIpc.RegisterAllSerializers(); - processStartInfo.EnvironmentVariables[$"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_PIPENAME}_{currentPID}"] = testHostControllerIpc.PipeName.Name; - List dataConsumersBuilder = []; - if (_testHostsInformation.DataConsumer.Length > 0) +#if NET8_0_OR_GREATER + IEnumerable arguments = partialCommandLine; +#else + string arguments = string.Join(" ", partialCommandLine); +#endif + ProcessStartInfo processStartInfo = new( + executableInfo.FileName, + arguments) { - dataConsumersBuilder.AddRange(_testHostsInformation.DataConsumer); - } + EnvironmentVariables = + { + { $"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_CORRELATIONID}_{currentPID}", processCorrelationId }, + { $"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_PARENTPID}_{currentPID}", processIdString }, + { $"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_SKIPEXTENSION}_{currentPID}", "1" }, + { $"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_PIPENAME}_{currentPID}", testHostControllerIpc.PipeName.Name }, + }, +#if !NETCOREAPP + UseShellExecute = false, +#endif + }; + + List dataConsumersBuilder = [.. _testHostsInformation.DataConsumer]; IPlatformOutputDevice? display = ServiceProvider.GetServiceInternal(); - if (display is not null and IDataConsumer dataConsumerDisplay) + if (display is IDataConsumer dataConsumerDisplay) { dataConsumersBuilder.Add(dataConsumerDisplay); } @@ -206,8 +203,7 @@ protected override async Task InternalRunAsync() processStartInfo.EnvironmentVariables.Add($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_TESTHOSTPROCESSSTARTTIME}_{currentPID}", testHostProcessStartupTime); await _logger.LogDebugAsync($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_TESTHOSTPROCESSSTARTTIME}_{currentPID} '{testHostProcessStartupTime}'"); await _logger.LogDebugAsync($"Starting test host process"); - IProcess testHostProcess = process.Start(processStartInfo) - ?? throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.CannotStartProcessErrorMessage, processStartInfo.FileName)); + using IProcess testHostProcess = process.Start(processStartInfo); testHostProcess.Exited += (sender, e) => { @@ -215,7 +211,15 @@ protected override async Task InternalRunAsync() _logger.LogDebug($"Test host process exited, PID: '{processExited?.Id}'"); }; - await _logger.LogDebugAsync($"Started test host process '{testHostProcess.Id}' HasExited: {testHostProcess.HasExited}"); + try + { + await _logger.LogDebugAsync($"Started test host process '{testHostProcess.Id}' HasExited: {testHostProcess.HasExited}"); + } + catch (InvalidOperationException) when (testHostProcess.HasExited) + { + // Access PID can throw InvalidOperationException if the process has already exited: + // System.InvalidOperationException: No process is associated with this object. + } string? seconds = configuration[PlatformConfigurationConstants.PlatformTestHostControllersManagerSingleConnectionNamedPipeServerWaitConnectionTimeoutSeconds]; int timeoutSeconds = seconds is null ? TimeoutHelper.DefaultHangTimeoutSeconds : int.Parse(seconds, CultureInfo.InvariantCulture); @@ -300,7 +304,7 @@ protected override async Task InternalRunAsync() await platformOutputDevice.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(string.Format(CultureInfo.InvariantCulture, PlatformResources.TestProcessDidNotExitGracefullyErrorMessage, exitCode))); } - await _logger.LogInformationAsync($"TestHostControllersTestHost ended with exit code '{exitCode}' (real test host exit code '{testHostProcess?.ExitCode}')' in '{consoleRunStarted.Elapsed}'"); + await _logger.LogInformationAsync($"TestHostControllersTestHost ended with exit code '{exitCode}' (real test host exit code '{testHostProcess.ExitCode}')' in '{consoleRunStarted.Elapsed}'"); await DisposeHelper.DisposeAsync(testHostControllerIpc); } finally diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/ToolsTestHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/ToolsTestHost.cs index dedf1e6887..d4a53a97f2 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/ToolsTestHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/ToolsTestHost.cs @@ -19,16 +19,12 @@ namespace Microsoft.Testing.Platform.Hosts; internal sealed class ToolsTestHost( ToolsInformation toolsInformation, ServiceProvider serviceProvider, - CommandLineParseResult parseResult, - ICommandLineOptionsProvider[] extensionsCommandLineOptionsProviders, - ICommandLineHandler commandLineHandler, + CommandLineHandler commandLineHandler, IPlatformOutputDevice platformOutputDevice) : ITestHost, IOutputDeviceDataProducer { private readonly ToolsInformation _toolsInformation = toolsInformation; private readonly ServiceProvider _serviceProvider = serviceProvider; - private readonly CommandLineParseResult _parseResult = parseResult; - private readonly ICommandLineOptionsProvider[] _extensionsCommandLineOptionsProviders = extensionsCommandLineOptionsProviders; - private readonly ICommandLineHandler _commandLineHandler = commandLineHandler; + private readonly CommandLineHandler _commandLineHandler = commandLineHandler; private readonly IPlatformOutputDevice _platformOutputDevice = platformOutputDevice; /// @@ -50,12 +46,12 @@ public async Task RunAsync() { IConsole console = _serviceProvider.GetConsole(); - if (_parseResult.ToolName is null) + if (_commandLineHandler.ParseResult.ToolName is null) { throw new InvalidOperationException("Tool name is null."); } - string toolNameToRun = _parseResult.ToolName; + string toolNameToRun = _commandLineHandler.ParseResult.ToolName; // TODO: Apply the override or do not support it for Tools? // TODO: Verify reserved tool names? @@ -100,13 +96,13 @@ private bool UnknownOptions([NotNullWhen(true)] out string? error, ITool tool) error = null; // This is unexpected - if (_parseResult is null) + if (_commandLineHandler.ParseResult is null) { return false; } StringBuilder stringBuilder = new(); - foreach (OptionRecord optionRecord in _parseResult.Options) + foreach (OptionRecord optionRecord in _commandLineHandler.ParseResult.Options) { if (!GetAllCommandLineOptionsProviderByOptionName(optionRecord.Option).Any()) { @@ -129,14 +125,8 @@ private bool ExtensionArgumentArityAreInvalid([NotNullWhen(true)] out string? er { error = null; - // This is unexpected - if (_parseResult is null) - { - return false; - } - StringBuilder stringBuilder = new(); - foreach (IGrouping optionRecord in _parseResult.Options.GroupBy(x => x.Option)) + foreach (IGrouping optionRecord in _commandLineHandler.ParseResult.Options.GroupBy(x => x.Option)) { string optionName = optionRecord.Key; int arity = optionRecord.Count(); @@ -150,11 +140,11 @@ private bool ExtensionArgumentArityAreInvalid([NotNullWhen(true)] out string? er } } - foreach (ICommandLineOptionsProvider extension in _extensionsCommandLineOptionsProviders) + foreach (ICommandLineOptionsProvider extension in _commandLineHandler.ExtensionsCommandLineOptionsProviders) { foreach (CommandLineOption option in extension.GetCommandLineOptions()) { - if (_parseResult.Options.Count(x => x.Option == option.Name) < option.Arity.Min) + if (_commandLineHandler.ParseResult.Options.Count(x => x.Option == option.Name) < option.Arity.Min) { stringBuilder.AppendLine( CultureInfo.InvariantCulture, @@ -174,14 +164,8 @@ private bool ExtensionArgumentArityAreInvalid([NotNullWhen(true)] out string? er private async Task ValidateOptionsArgumentsAsync(ITool tool) { - // This is unexpected - if (_parseResult is null) - { - return ValidationResult.Invalid("Parse result should not be null"); - } - StringBuilder stringBuilder = new(); - foreach (OptionRecord optionRecord in _parseResult.Options) + foreach (OptionRecord optionRecord in _commandLineHandler.ParseResult.Options) { ICommandLineOptionsProvider extension = GetAllCommandLineOptionsProviderByOptionName(optionRecord.Option).Single(); ValidationResult result = await extension.ValidateOptionArgumentsAsync(extension.GetCommandLineOptions().Single(x => x.Name == optionRecord.Option), optionRecord.Arguments); @@ -198,7 +182,7 @@ private async Task ValidateOptionsArgumentsAsync(ITool tool) private IEnumerable GetAllCommandLineOptionsProviderByOptionName(string optionName) { - foreach (ICommandLineOptionsProvider commandLineOptionsProvider in _extensionsCommandLineOptionsProviders) + foreach (ICommandLineOptionsProvider commandLineOptionsProvider in _commandLineHandler.ExtensionsCommandLineOptionsProviders) { if (commandLineOptionsProvider.GetCommandLineOptions().Any(option => option.Name == optionName)) { diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/IClient.cs b/src/Platform/Microsoft.Testing.Platform/IPC/IClient.cs index 4ab0ffe768..414d847e1c 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/IClient.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/IClient.cs @@ -3,12 +3,11 @@ namespace Microsoft.Testing.Platform.IPC; -internal interface IClient : IDisposable +internal interface IClient : #if NETCOREAPP -#pragma warning disable SA1001 // Commas should be spaced correctly - , IAsyncDisposable -#pragma warning restore SA1001 // Commas should be spaced correctly + IAsyncDisposable, #endif + IDisposable { bool IsConnected { get; } diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/INamedPipeIC.cs b/src/Platform/Microsoft.Testing.Platform/IPC/INamedPipeBase.cs similarity index 75% rename from src/Platform/Microsoft.Testing.Platform/IPC/INamedPipeIC.cs rename to src/Platform/Microsoft.Testing.Platform/IPC/INamedPipeBase.cs index dca6115b1d..f8c85dc0de 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/INamedPipeIC.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/INamedPipeBase.cs @@ -5,5 +5,5 @@ namespace Microsoft.Testing.Platform.IPC; internal interface INamedPipeBase { - void RegisterSerializer(INamedPipeSerializer namedPipeSerializer); + void RegisterSerializer(INamedPipeSerializer namedPipeSerializer, Type type); } diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/IRequest.cs b/src/Platform/Microsoft.Testing.Platform/IPC/IRequest.cs index 59fb95fe47..ef3f1a00fc 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/IRequest.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/IRequest.cs @@ -3,6 +3,4 @@ namespace Microsoft.Testing.Platform.IPC; -internal interface IRequest -{ -} +internal interface IRequest; diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/IResponse.cs b/src/Platform/Microsoft.Testing.Platform/IPC/IResponse.cs index 7843fba738..0652c8e1be 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/IResponse.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/IResponse.cs @@ -3,6 +3,4 @@ namespace Microsoft.Testing.Platform.IPC; -internal interface IResponse -{ -} +internal interface IResponse; diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/IServer.cs b/src/Platform/Microsoft.Testing.Platform/IPC/IServer.cs index 8eec75218c..70951d2e81 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/IServer.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/IServer.cs @@ -3,12 +3,11 @@ namespace Microsoft.Testing.Platform.IPC; -internal interface IServer : INamedPipeBase, IDisposable +internal interface IServer : INamedPipeBase, #if NETCOREAPP -#pragma warning disable SA1001 // Commas should be spaced correctly - , IAsyncDisposable -#pragma warning restore SA1001 // Commas should be spaced correctly + IAsyncDisposable, #endif + IDisposable { PipeNameDescription PipeName { get; } diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/Models/CommandLineOptionMessages.cs b/src/Platform/Microsoft.Testing.Platform/IPC/Models/CommandLineOptionMessages.cs new file mode 100644 index 0000000000..8c0bb9befb --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/IPC/Models/CommandLineOptionMessages.cs @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform.IPC.Models; + +internal sealed record CommandLineOptionMessage(string Name, string Description, bool IsHidden, bool IsBuiltIn) : IRequest; + +internal sealed record CommandLineOptionMessages(string ModuleName, CommandLineOptionMessage[] CommandLineOptionMessageList) : IRequest; diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeBase.cs b/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeBase.cs index 1f10813c9d..9f75028cd4 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeBase.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeBase.cs @@ -14,33 +14,25 @@ internal abstract class NamedPipeBase private readonly Dictionary _typeSerializer = []; private readonly Dictionary _idSerializer = []; - public void RegisterSerializer(INamedPipeSerializer namedPipeSerializer) + public void RegisterSerializer(INamedPipeSerializer namedPipeSerializer, Type type) { - _typeSerializer.Add(typeof(T), namedPipeSerializer); + _typeSerializer.Add(type, namedPipeSerializer); _idSerializer.Add(namedPipeSerializer.Id, namedPipeSerializer); } protected INamedPipeSerializer GetSerializer(int id) => _idSerializer.TryGetValue(id, out object? serializer) ? (INamedPipeSerializer)serializer - : throw new InvalidOperationException(string.Format( + : throw new ArgumentException(string.Format( CultureInfo.InvariantCulture, -#if PLATFORM_MSBUILD - "No serializer registered with id '{0}'", -#else PlatformResources.NoSerializerRegisteredWithIdErrorMessage, -#endif id)); protected INamedPipeSerializer GetSerializer(Type type) => _typeSerializer.TryGetValue(type, out object? serializer) ? (INamedPipeSerializer)serializer - : throw new InvalidOperationException(string.Format( + : throw new ArgumentException(string.Format( CultureInfo.InvariantCulture, -#if PLATFORM_MSBUILD - "No serializer registered with type '{0}'", -#else PlatformResources.NoSerializerRegisteredWithTypeErrorMessage, -#endif type)); } diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeClient.cs b/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeClient.cs index ad46e498ca..a68a325de6 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeClient.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeClient.cs @@ -135,7 +135,6 @@ public async Task RequestReplyAsync(TRequest req int missingBytesToReadOfWholeMessage = 0; while (true) { - int missingBytesToReadOfCurrentChunk = 0; int currentReadIndex = 0; #if NETCOREAPP int currentReadBytes = await _namedPipeClientStream.ReadAsync(_readBuffer.AsMemory(currentReadIndex, _readBuffer.Length), cancellationToken); @@ -144,7 +143,7 @@ public async Task RequestReplyAsync(TRequest req #endif // Reset the current chunk size - missingBytesToReadOfCurrentChunk = currentReadBytes; + int missingBytesToReadOfCurrentChunk = currentReadBytes; // If currentRequestSize is 0, we need to read the message size if (currentMessageSize == 0) diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeServer.cs b/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeServer.cs index 66162fae7d..1eb2eb56f4 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeServer.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeServer.cs @@ -71,7 +71,7 @@ public NamedPipeServer( _cancellationToken = cancellationToken; } - public PipeNameDescription PipeName { get; private set; } + public PipeNameDescription PipeName { get; } public bool WasConnected { get; private set; } @@ -113,7 +113,6 @@ private async Task InternalLoopAsync(CancellationToken cancellationToken) int missingBytesToReadOfWholeMessage = 0; while (true) { - int missingBytesToReadOfCurrentChunk = 0; int currentReadIndex = 0; #if NET int currentReadBytes = await _namedPipeServerStream.ReadAsync(_readBuffer.AsMemory(currentReadIndex, _readBuffer.Length), cancellationToken); @@ -127,7 +126,7 @@ private async Task InternalLoopAsync(CancellationToken cancellationToken) } // Reset the current chunk size - missingBytesToReadOfCurrentChunk = currentReadBytes; + int missingBytesToReadOfCurrentChunk = currentReadBytes; // If currentRequestSize is 0, we need to read the message size if (currentMessageSize == 0) diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/ObjectFieldIds.cs b/src/Platform/Microsoft.Testing.Platform/IPC/ObjectFieldIds.cs new file mode 100644 index 0000000000..f4e31602c5 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/IPC/ObjectFieldIds.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform.IPC; + +// WARNING: Please note this file needs to be kept aligned with the one in the dotnet sdk. +// The protocol follows the concept of optional properties. +// The id is used to identify the property in the stream and it will be skipped if it's not recognized. +// We can add new properties with new ids, but we CANNOT change the existing ids (to support backwards compatibility). +internal static class CommandLineOptionMessagesFieldsId +{ + internal const int ModuleName = 1; + internal const int CommandLineOptionMessageList = 2; +} + +internal static class CommandLineOptionMessageFieldsId +{ + internal const int Name = 1; + internal const int Description = 2; + internal const int IsHidden = 3; + internal const int IsBuiltIn = 4; +} diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs b/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs index 264a2b81b6..66b2df0978 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs @@ -4,13 +4,14 @@ #if NETCOREAPP using System.Buffers; #endif -using System.Text; #if NET using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Resources; #endif +using System.Text; + namespace Microsoft.Testing.Platform.IPC.Serializers; internal abstract class BaseSerializer @@ -18,13 +19,21 @@ internal abstract class BaseSerializer #if NETCOREAPP protected static string ReadString(Stream stream) { - Span len = stackalloc byte[4]; - stream.Read(len); + Span len = stackalloc byte[sizeof(int)]; +#if NET7_0_OR_GREATER + stream.ReadExactly(len); +#else + _ = stream.Read(len); +#endif int stringLen = BitConverter.ToInt32(len); byte[] bytes = ArrayPool.Shared.Rent(stringLen); try { - stream.Read(bytes, 0, stringLen); +#if NET7_0_OR_GREATER + stream.ReadExactly(bytes, 0, stringLen); +#else + _ = stream.Read(bytes, 0, stringLen); +#endif return Encoding.UTF8.GetString(bytes, 0, stringLen); } finally @@ -39,7 +48,7 @@ protected static void WriteString(Stream stream, string str) byte[] bytes = ArrayPool.Shared.Rent(stringutf8TotalBytes); try { - Span len = stackalloc byte[4]; + Span len = stackalloc byte[sizeof(int)]; ApplicationStateGuard.Ensure(BitConverter.TryWriteBytes(len, stringutf8TotalBytes), PlatformResources.UnexpectedExceptionDuringByteConversionErrorMessage); stream.Write(len); @@ -52,6 +61,27 @@ protected static void WriteString(Stream stream, string str) } } + protected static void WriteStringSize(Stream stream, string str) + { + int stringutf8TotalBytes = Encoding.UTF8.GetByteCount(str); + Span len = stackalloc byte[sizeof(int)]; + + ApplicationStateGuard.Ensure(BitConverter.TryWriteBytes(len, stringutf8TotalBytes), PlatformResources.UnexpectedExceptionDuringByteConversionErrorMessage); + + stream.Write(len); + } + + protected static void WriteSize(Stream stream) + where T : struct + { + int sizeInBytes = GetSize(); + Span len = stackalloc byte[sizeof(int)]; + + ApplicationStateGuard.Ensure(BitConverter.TryWriteBytes(len, sizeInBytes), PlatformResources.UnexpectedExceptionDuringByteConversionErrorMessage); + + stream.Write(len); + } + protected static void WriteInt(Stream stream, int value) { Span bytes = stackalloc byte[sizeof(int)]; @@ -68,28 +98,75 @@ protected static void WriteLong(Stream stream, long value) stream.Write(bytes); } + protected static void WriteShort(Stream stream, ushort value) + { + Span bytes = stackalloc byte[sizeof(ushort)]; + ApplicationStateGuard.Ensure(BitConverter.TryWriteBytes(bytes, value), PlatformResources.UnexpectedExceptionDuringByteConversionErrorMessage); + + stream.Write(bytes); + } + + protected static void WriteBool(Stream stream, bool value) + { + Span bytes = stackalloc byte[sizeof(bool)]; + ApplicationStateGuard.Ensure(BitConverter.TryWriteBytes(bytes, value), PlatformResources.UnexpectedExceptionDuringByteConversionErrorMessage); + + stream.Write(bytes); + } + protected static int ReadInt(Stream stream) { Span bytes = stackalloc byte[sizeof(int)]; - stream.Read(bytes); +#if NET7_0_OR_GREATER + stream.ReadExactly(bytes); +#else + _ = stream.Read(bytes); +#endif return BitConverter.ToInt32(bytes); } protected static long ReadLong(Stream stream) { Span bytes = stackalloc byte[sizeof(long)]; - stream.Read(bytes); +#if NET7_0_OR_GREATER + stream.ReadExactly(bytes); +#else + _ = stream.Read(bytes); +#endif return BitConverter.ToInt64(bytes); } + protected static ushort ReadShort(Stream stream) + { + Span bytes = stackalloc byte[sizeof(ushort)]; +#if NET7_0_OR_GREATER + stream.ReadExactly(bytes); +#else + _ = stream.Read(bytes); +#endif + return BitConverter.ToUInt16(bytes); + } + + protected static bool ReadBool(Stream stream) + { + Span bytes = stackalloc byte[sizeof(bool)]; +#if NET7_0_OR_GREATER + stream.ReadExactly(bytes); +#else + _ = stream.Read(bytes); +#endif + return BitConverter.ToBoolean(bytes); + } + #else protected static string ReadString(Stream stream) { - byte[] len = new byte[4]; - stream.Read(len, 0, len.Length); + byte[] len = new byte[sizeof(int)]; + _ = stream.Read(len, 0, len.Length); int length = BitConverter.ToInt32(len, 0); byte[] bytes = new byte[length]; - stream.Read(bytes, 0, bytes.Length); + _ = stream.Read(bytes, 0, bytes.Length); + return Encoding.UTF8.GetString(bytes); } @@ -101,6 +178,21 @@ protected static void WriteString(Stream stream, string str) stream.Write(bytes, 0, bytes.Length); } + protected static void WriteStringSize(Stream stream, string str) + { + byte[] bytes = Encoding.UTF8.GetBytes(str); + byte[] len = BitConverter.GetBytes(bytes.Length); + stream.Write(len, 0, len.Length); + } + + protected static void WriteSize(Stream stream) + where T : struct + { + int sizeInBytes = GetSize(); + byte[] len = BitConverter.GetBytes(sizeInBytes); + stream.Write(len, 0, sizeInBytes); + } + protected static void WriteInt(Stream stream, int value) { byte[] bytes = BitConverter.GetBytes(value); @@ -109,8 +201,8 @@ protected static void WriteInt(Stream stream, int value) protected static int ReadInt(Stream stream) { - byte[] bytes = new byte[4]; - stream.Read(bytes, 0, bytes.Length); + byte[] bytes = new byte[sizeof(int)]; + _ = stream.Read(bytes, 0, bytes.Length); return BitConverter.ToInt32(bytes, 0); } @@ -120,11 +212,75 @@ protected static void WriteLong(Stream stream, long value) stream.Write(bytes, 0, bytes.Length); } + protected static void WriteShort(Stream stream, ushort value) + { + byte[] bytes = BitConverter.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + protected static long ReadLong(Stream stream) { byte[] bytes = new byte[sizeof(long)]; - stream.Read(bytes, 0, bytes.Length); + _ = stream.Read(bytes, 0, bytes.Length); return BitConverter.ToInt64(bytes, 0); } + + protected static ushort ReadShort(Stream stream) + { + byte[] bytes = new byte[sizeof(ushort)]; + _ = stream.Read(bytes, 0, bytes.Length); + return BitConverter.ToUInt16(bytes, 0); + } + + protected static void WriteBool(Stream stream, bool value) + { + byte[] bytes = BitConverter.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + + protected static bool ReadBool(Stream stream) + { + byte[] bytes = new byte[sizeof(bool)]; + _ = stream.Read(bytes, 0, bytes.Length); + return BitConverter.ToBoolean(bytes, 0); + } #endif + + protected static void WriteField(Stream stream, ushort id, string? value) + { + if (value is null) + { + return; + } + + WriteShort(stream, id); + WriteStringSize(stream, value); + WriteString(stream, value); + } + + protected static void WriteField(Stream stream, ushort id, bool value) + { + WriteShort(stream, id); + WriteSize(stream); + WriteBool(stream, value); + } + + protected static void SetPosition(Stream stream, long position) => stream.Position = position; + + protected static void WriteAtPosition(Stream stream, int value, long position) + { + long currentPosition = stream.Position; + SetPosition(stream, position); + WriteInt(stream, value); + SetPosition(stream, currentPosition); + } + + private static int GetSize() => typeof(T) switch + { + Type type when type == typeof(int) => sizeof(int), + Type type when type == typeof(long) => sizeof(long), + Type type when type == typeof(short) => sizeof(short), + Type type when type == typeof(bool) => sizeof(bool), + _ => 0, + }; } diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/CommandLineOptionMessagesSerializer.cs b/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/CommandLineOptionMessagesSerializer.cs new file mode 100644 index 0000000000..b801628c09 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/CommandLineOptionMessagesSerializer.cs @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.IPC.Models; + +namespace Microsoft.Testing.Platform.IPC.Serializers; + +/* +|---FieldCount---| 2 bytes + +|---ModuleName Id---| 1 (2 bytes) +|---ModuleName Size---| (4 bytes) +|---ModuleName Value---| (n bytes) + +|---CommandLineOptionMessageList Id---| 2 (2 bytes) +|---CommandLineOptionMessageList Size---| (4 bytes) +|---CommandLineOptionMessageList Value---| (n bytes) + |---CommandLineOptionMessageList Length---| (4 bytes) + + |---CommandLineOptionMessageList[0] FieldCount---| 2 bytes + + |---CommandLineOptionMessageList[0] Name Id---| 1 (2 bytes) + |---CommandLineOptionMessageList[0] Name Size---| (4 bytes) + |---CommandLineOptionMessageList[0] Name Value---| (n bytes) + + |---CommandLineOptionMessageList[1] Description Id---| 2 (2 bytes) + |---CommandLineOptionMessageList[1] Description Size---| (4 bytes) + |---CommandLineOptionMessageList[1] Description Value---| (n bytes) + + |---CommandLineOptionMessageList[3] IsHidden Id---| 4 (2 bytes) + |---CommandLineOptionMessageList[3] IsHidden Size---| (4 bytes) + |---CommandLineOptionMessageList[3] IsHidden Value---| (1 byte) + + |---CommandLineOptionMessageList[4] IsBuiltIn Id---| 5 (2 bytes) + |---CommandLineOptionMessageList[4] IsBuiltIn Size---| (4 bytes) + |---CommandLineOptionMessageList[4] IsBuiltIn Value---| (1 byte) +*/ + +internal sealed class CommandLineOptionMessagesSerializer : BaseSerializer, INamedPipeSerializer +{ + public int Id => 3; + + public object Deserialize(Stream stream) + { + string moduleName = string.Empty; + List? commandLineOptionMessages = null; + + ushort fieldCount = ReadShort(stream); + + for (int i = 0; i < fieldCount; i++) + { + int fieldId = ReadShort(stream); + int fieldSize = ReadInt(stream); + + switch (fieldId) + { + case CommandLineOptionMessagesFieldsId.ModuleName: + moduleName = ReadString(stream); + break; + + case CommandLineOptionMessagesFieldsId.CommandLineOptionMessageList: + commandLineOptionMessages = ReadCommandLineOptionMessagesPayload(stream); + break; + + default: + // If we don't recognize the field id, skip the payload corresponding to that field + SetPosition(stream, stream.Position + fieldSize); + break; + } + } + + return new CommandLineOptionMessages(moduleName, commandLineOptionMessages is null ? [] : [.. commandLineOptionMessages]); + } + + private static List ReadCommandLineOptionMessagesPayload(Stream stream) + { + List commandLineOptionMessages = []; + + int length = ReadInt(stream); + for (int i = 0; i < length; i++) + { + string name = string.Empty, description = string.Empty; + bool isHidden = false, isBuiltIn = false; + + int fieldCount = ReadShort(stream); + + for (int j = 0; j < fieldCount; j++) + { + int fieldId = ReadShort(stream); + int fieldSize = ReadInt(stream); + + switch (fieldId) + { + case CommandLineOptionMessageFieldsId.Name: + name = ReadString(stream); + break; + + case CommandLineOptionMessageFieldsId.Description: + description = ReadString(stream); + break; + + case CommandLineOptionMessageFieldsId.IsHidden: + isHidden = ReadBool(stream); + break; + + case CommandLineOptionMessageFieldsId.IsBuiltIn: + isBuiltIn = ReadBool(stream); + break; + + default: + SetPosition(stream, stream.Position + fieldSize); + break; + } + } + + commandLineOptionMessages.Add(new CommandLineOptionMessage(name, description, isHidden, isBuiltIn)); + } + + return commandLineOptionMessages; + } + + public void Serialize(object objectToSerialize, Stream stream) + { + RoslynDebug.Assert(stream.CanSeek, "We expect a seekable stream."); + + var commandLineOptionMessages = (CommandLineOptionMessages)objectToSerialize; + + WriteShort(stream, GetFieldCount(commandLineOptionMessages)); + + WriteField(stream, CommandLineOptionMessagesFieldsId.ModuleName, commandLineOptionMessages.ModuleName); + WriteCommandLineOptionMessagesPayload(stream, commandLineOptionMessages.CommandLineOptionMessageList); + } + + private static void WriteCommandLineOptionMessagesPayload(Stream stream, CommandLineOptionMessage[] commandLineOptionMessageList) + { + if (IsNull(commandLineOptionMessageList)) + { + return; + } + + WriteShort(stream, CommandLineOptionMessagesFieldsId.CommandLineOptionMessageList); + + // We will reserve an int (4 bytes) + // so that we fill the size later, once we write the payload + WriteInt(stream, 0); + + long before = stream.Position; + WriteInt(stream, commandLineOptionMessageList.Length); + foreach (CommandLineOptionMessage commandLineOptionMessage in commandLineOptionMessageList) + { + WriteShort(stream, GetFieldCount(commandLineOptionMessage)); + + WriteField(stream, CommandLineOptionMessageFieldsId.Name, commandLineOptionMessage.Name); + WriteField(stream, CommandLineOptionMessageFieldsId.Description, commandLineOptionMessage.Description); + WriteField(stream, CommandLineOptionMessageFieldsId.IsHidden, commandLineOptionMessage.IsHidden); + WriteField(stream, CommandLineOptionMessageFieldsId.IsBuiltIn, commandLineOptionMessage.IsBuiltIn); + } + + // NOTE: We are able to seek only if we are using a MemoryStream + // thus, the seek operation is fast as we are only changing the value of a property + WriteAtPosition(stream, (int)(stream.Position - before), before - sizeof(int)); + } + + private static ushort GetFieldCount(CommandLineOptionMessages commandLineOptionMessages) => + (ushort)((RoslynString.IsNullOrEmpty(commandLineOptionMessages.ModuleName) ? 0 : 1) + + (commandLineOptionMessages is null ? 0 : 1)); + + private static ushort GetFieldCount(CommandLineOptionMessage commandLineOptionMessage) => + (ushort)((RoslynString.IsNullOrEmpty(commandLineOptionMessage.Name) ? 0 : 1) + + (RoslynString.IsNullOrEmpty(commandLineOptionMessage.Description) ? 0 : 1) + + 2); + + private static bool IsNull(T[] items) => items is null || items.Length == 0; +} diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/RegisterSerializers.cs b/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/RegisterSerializers.cs index db7c2896df..278999e70c 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/RegisterSerializers.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/RegisterSerializers.cs @@ -5,12 +5,22 @@ namespace Microsoft.Testing.Platform.IPC.Serializers; +/* + * NOTE: We have the following ids used for those serializers + * DO NOT change the IDs of the existing serializers + * VoidResponseSerializer: 0 + * TestHostProcessExitRequestSerializer: 1 + * TestHostProcessPIDRequestSerializer: 2 + * CommandLineOptionMessagesSerializer: 3 + * ModuleSerializer: 4 +*/ internal static class RegisterSerializers { public static void RegisterAllSerializers(this NamedPipeBase namedPipeBase) { - namedPipeBase.RegisterSerializer(new VoidResponseSerializer()); - namedPipeBase.RegisterSerializer(new TestHostProcessExitRequestSerializer()); - namedPipeBase.RegisterSerializer(new TestHostProcessPIDRequestSerializer()); + namedPipeBase.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + namedPipeBase.RegisterSerializer(new TestHostProcessExitRequestSerializer(), typeof(TestHostProcessExitRequest)); + namedPipeBase.RegisterSerializer(new TestHostProcessPIDRequestSerializer(), typeof(TestHostProcessPIDRequest)); + namedPipeBase.RegisterSerializer(new CommandLineOptionMessagesSerializer(), typeof(CommandLineOptionMessages)); } } diff --git a/src/Platform/Microsoft.Testing.Platform/Logging/ILogger.cs b/src/Platform/Microsoft.Testing.Platform/Logging/ILogger.cs index 136c87621b..75a235dea9 100644 --- a/src/Platform/Microsoft.Testing.Platform/Logging/ILogger.cs +++ b/src/Platform/Microsoft.Testing.Platform/Logging/ILogger.cs @@ -41,6 +41,4 @@ public interface ILogger /// Represents a logger that can be used for logging messages with a specific category. /// /// The type of the category name. -public interface ILogger : ILogger -{ -} +public interface ILogger : ILogger; diff --git a/src/Platform/Microsoft.Testing.Platform/Logging/LoggingManager.cs b/src/Platform/Microsoft.Testing.Platform/Logging/LoggingManager.cs index 2cd3a124ac..89f949d2fa 100644 --- a/src/Platform/Microsoft.Testing.Platform/Logging/LoggingManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/Logging/LoggingManager.cs @@ -28,10 +28,7 @@ internal async Task BuildAsync(IServiceProvider serviceProvider, continue; } - if (serviceInstance is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await serviceInstance.TryInitializeAsync(); loggerProviders.Add(serviceInstance); } diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/DataWithSessionUid.cs b/src/Platform/Microsoft.Testing.Platform/Messages/DataWithSessionUid.cs index f0941c3f5d..52f1d8b61b 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/DataWithSessionUid.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/DataWithSessionUid.cs @@ -50,7 +50,7 @@ public override string ToString() builder.Append(','); } - builder.Append(' ').Append(property.ToString()); + builder.Append(' ').Append(property); } if (hasAnyProperty) diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/FileArtifacts.cs b/src/Platform/Microsoft.Testing.Platform/Messages/FileArtifacts.cs index bb6db28f58..a6137bf7dc 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/FileArtifacts.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/FileArtifacts.cs @@ -52,7 +52,7 @@ public override string ToString() builder.Append(','); } - builder.Append(' ').Append(property.ToString()); + builder.Append(' ').Append(property); } if (hasAnyProperty) @@ -112,7 +112,7 @@ public override string ToString() builder.Append(','); } - builder.Append(' ').Append(property.ToString()); + builder.Append(' ').Append(property); } if (hasAnyProperty) @@ -177,7 +177,7 @@ public override string ToString() builder.Append(','); } - builder.Append(' ').Append(property.ToString()); + builder.Append(' ').Append(property); } if (hasAnyProperty) @@ -190,7 +190,7 @@ public override string ToString() .Append(", FilePath = ") .Append(FileInfo.FullName) .Append(", TestNode = ") - .Append(TestNode.ToString()) + .Append(TestNode) .Append(" }"); return builder.ToString(); diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/ListTestsMessageBus.cs b/src/Platform/Microsoft.Testing.Platform/Messages/ListTestsMessageBus.cs index c2f63835dd..629e0a059c 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/ListTestsMessageBus.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/ListTestsMessageBus.cs @@ -13,7 +13,7 @@ namespace Microsoft.Testing.Platform.Messages; internal sealed class ListTestsMessageBus( - ITestFramework testFrameworkAdapter, + ITestFramework testFramework, ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource, ILoggerFactory loggerFactory, IOutputDevice outputDisplay, @@ -21,7 +21,7 @@ internal sealed class ListTestsMessageBus( IEnvironment environment, ITestApplicationProcessExitCode testApplicationProcessExitCode) : BaseMessageBus, IMessageBus, IDisposable, IOutputDeviceDataProducer { - private readonly ITestFramework _testFrameworkAdapter = testFrameworkAdapter; + private readonly ITestFramework _testFramework = testFramework; private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; private readonly IOutputDevice _outputDisplay = outputDisplay; private readonly IEnvironment _environment = environment; @@ -30,7 +30,7 @@ internal sealed class ListTestsMessageBus( private readonly IAsyncMonitor _asyncMonitor = asyncMonitorFactory.Create(); private bool _printTitle = true; - public override IDataConsumer[] DataConsumerServices => Array.Empty(); + public override IDataConsumer[] DataConsumerServices => []; public string Uid => nameof(ListTestsMessageBus); @@ -59,7 +59,7 @@ public override async Task PublishAsync(IDataProducer dataProducer, IData data) return; } - if (_testFrameworkAdapter.Uid != dataProducer.Uid + if (_testFramework.Uid != dataProducer.Uid || data is not TestNodeUpdateMessage testNodeUpdatedMessage || testNodeUpdatedMessage.TestNode.Properties.SingleOrDefault() is not DiscoveredTestNodeStateProperty) { diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBagData.cs b/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBagData.cs index 134ac5ddd6..e438002206 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBagData.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBagData.cs @@ -33,7 +33,7 @@ public override string ToString() builder.Append(','); } - builder.Append(' ').Append(property.ToString()); + builder.Append(' ').Append(property); } if (hasAnyProperty) diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/TestNode.cs b/src/Platform/Microsoft.Testing.Platform/Messages/TestNode.cs index 33336c9ace..ff3bc023ed 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/TestNode.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/TestNode.cs @@ -34,7 +34,7 @@ public override string ToString() builder.Append(','); } - builder.Append(' ').Append(property.ToString()); + builder.Append(' ').Append(property); } if (hasAnyProperty) diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.Categories.cs b/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.Categories.cs index 6b02414fa7..4ad215da6c 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.Categories.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.Categories.cs @@ -15,7 +15,6 @@ internal static class TestNodePropertiesCategories public static Type[] WellKnownTestNodeTestRunOutcomeProperties { get; } = [ typeof(PassedTestNodeStateProperty), - typeof(SkippedTestNodeStateProperty), typeof(FailedTestNodeStateProperty), typeof(ErrorTestNodeStateProperty), diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.cs b/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.cs index fb2904ebe3..72394ee640 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.cs @@ -8,9 +8,7 @@ namespace Microsoft.Testing.Platform.Extensions.Messages; /// /// The interface that every test node property must implement. /// -public interface IProperty -{ -} +public interface IProperty; /// /// Node property that represents a key-value pair. diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeUpdateMessage.cs b/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeUpdateMessage.cs index 79f709b5f1..28604fb2f4 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeUpdateMessage.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeUpdateMessage.cs @@ -34,7 +34,7 @@ public override string ToString() builder.Append(','); } - builder.Append(' ').Append(property.ToString()); + builder.Append(' ').Append(property); } if (hasAnyProperty) @@ -45,7 +45,7 @@ public override string ToString() builder.Append("], ParentTestNodeUid = ") .Append(ParentTestNodeUid?.ToString() ?? "") .Append(", TestNode = ") - .Append(TestNode.ToString()) + .Append(TestNode) .Append(" }"); return builder.ToString(); diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/TestRequestExecutionTimeInfo.cs b/src/Platform/Microsoft.Testing.Platform/Messages/TestRequestExecutionTimeInfo.cs index 66ceb743f5..02fc44e877 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/TestRequestExecutionTimeInfo.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/TestRequestExecutionTimeInfo.cs @@ -7,9 +7,9 @@ namespace Microsoft.Testing.Platform.Extensions.Messages; internal readonly struct TestRequestExecutionTimeInfo(TimingInfo timingInfo) : IData { - public readonly string DisplayName => nameof(TestRequestExecutionTimeInfo); + public string DisplayName => nameof(TestRequestExecutionTimeInfo); - public readonly string? Description => "Information about the test execution times."; + public string? Description => "Information about the test execution times."; public TimingInfo TimingInfo { get; } = timingInfo; diff --git a/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj b/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj index 10cc4c51ea..5f780a5381 100644 --- a/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj +++ b/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj @@ -1,11 +1,7 @@ - + $(MicrosoftTestingTargetFrameworks);netstandard2.0 - true - true - false - true @@ -15,7 +11,7 @@ - + @@ -105,8 +101,9 @@ This package provides the core platform and the .NET implementation of the proto + - + <_TemplateProperties>Version=$(Version) @@ -133,12 +130,4 @@ This package provides the core platform and the .NET implementation of the proto - - - - - - - - diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/ConsoleDisplayService.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/ConsoleDisplayService.cs index 1b2199d45f..1b461e05b9 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/ConsoleDisplayService.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/ConsoleDisplayService.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Globalization; -using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -11,6 +10,7 @@ using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Resources; using Microsoft.Testing.Platform.Services; using Microsoft.Testing.Platform.TestHost; using Microsoft.Testing.Platform.TestHostControllers; @@ -24,7 +24,6 @@ internal class ConsoleOutputDevice : IPlatformOutputDevice, { #pragma warning disable SA1310 // Field names should not contain underscore private const string TESTINGPLATFORM_CONSOLEOUTPUTDEVICE_SKIP_BANNER = nameof(TESTINGPLATFORM_CONSOLEOUTPUTDEVICE_SKIP_BANNER); - private const string BUILDTIME_ATTRIBUTE_NAME = "Microsoft.Testing.Platform.Application.BuildTimeUTC"; #pragma warning restore SA1310 // Field names should not contain underscore private readonly List _sessionFilesArtifact = []; @@ -36,6 +35,7 @@ internal class ConsoleOutputDevice : IPlatformOutputDevice, private readonly IRuntimeFeature _runtimeFeature; private readonly IEnvironment _environment; private readonly IProcessHandler _process; + private readonly IPlatformInformation _platformInformation; private readonly bool _isVSTestMode; private readonly bool _isListTests; private readonly bool _isServerMode; @@ -43,7 +43,6 @@ internal class ConsoleOutputDevice : IPlatformOutputDevice, private readonly ILogger? _logger; private readonly FileLoggerProvider? _fileLoggerProvider; private readonly bool _underProcessMonitor; - private static readonly char[] PlusSign = new[] { '+' }; private int _totalTests; private int _totalPassedTests; @@ -53,12 +52,10 @@ internal class ConsoleOutputDevice : IPlatformOutputDevice, private bool _bannerDisplayed; private TestRequestExecutionTimeInfo? _testRequestExecutionTimeInfo; - public ConsoleOutputDevice(ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource, IConsole console, ITestApplicationModuleInfo testApplicationModuleInfo, ITestHostControllerInfo testHostControllerInfo, IAsyncMonitor asyncMonitor, IRuntimeFeature runtimeFeature, IEnvironment environment, IProcessHandler process, - bool isVSTestMode, - bool isListTests, - bool isServerMode, - int minimumExpectedTest, - FileLoggerProvider? fileLoggerProvider) + public ConsoleOutputDevice(ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource, IConsole console, + ITestApplicationModuleInfo testApplicationModuleInfo, ITestHostControllerInfo testHostControllerInfo, IAsyncMonitor asyncMonitor, + IRuntimeFeature runtimeFeature, IEnvironment environment, IProcessHandler process, IPlatformInformation platformInformation, + bool isVSTestMode, bool isListTests, bool isServerMode, int minimumExpectedTest, FileLoggerProvider? fileLoggerProvider) { _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; _console = console; @@ -67,6 +64,7 @@ public ConsoleOutputDevice(ITestApplicationCancellationTokenSource testApplicati _runtimeFeature = runtimeFeature; _environment = environment; _process = process; + _platformInformation = platformInformation; _isVSTestMode = isVSTestMode; _isListTests = isListTests; _isServerMode = isServerMode; @@ -90,7 +88,7 @@ public ConsoleOutputDevice(ITestApplicationCancellationTokenSource testApplicati _bannerDisplayed = true; } - _testApplicationCancellationTokenSource.CancellationToken.Register(() => _console.WriteLine("Cancelling the test session...")); + _testApplicationCancellationTokenSource.CancellationToken.Register(() => _console.WriteLine(PlatformResources.CancellingTestSession)); } public Type[] DataTypesConsumed { get; } = @@ -125,7 +123,7 @@ private async Task LogDebugAsync(string message) } } - public virtual async Task DisplayBannerAsync() + public virtual async Task DisplayBannerAsync(string? bannerMessage) { if (_isVSTestMode) { @@ -141,44 +139,42 @@ public virtual async Task DisplayBannerAsync() _bannerDisplayed = true; - StringBuilder stringBuilder = new(); - stringBuilder.Append(".NET Testing Platform"); - if (_runtimeFeature.IsDynamicCodeSupported) + if (bannerMessage is not null) { - AssemblyInformationalVersionAttribute? version = Assembly.GetExecutingAssembly().GetCustomAttribute(); - if (version is not null) + _console.WriteLine(bannerMessage); + } + else + { + StringBuilder stringBuilder = new(); + stringBuilder.Append(_platformInformation.Name); + + if (_platformInformation.Version is { } version) { - string informationalVersion = version.InformationalVersion; - int index = informationalVersion.LastIndexOfAny(PlusSign); - if (index != -1) - { - stringBuilder.Append(CultureInfo.InvariantCulture, $" v{informationalVersion[..(index + 10)]}"); - } - else + stringBuilder.Append(CultureInfo.InvariantCulture, $" v{version}"); + if (_platformInformation.CommitHash is { } commitHash) { - stringBuilder.Append(CultureInfo.InvariantCulture, $" v{informationalVersion}"); + stringBuilder.Append(CultureInfo.InvariantCulture, $"+{commitHash[..10]}"); } + } - AssemblyMetadataAttribute? buildTime = Assembly.GetExecutingAssembly() - .GetCustomAttributes() - .FirstOrDefault(x => x.Key == BUILDTIME_ATTRIBUTE_NAME); - - if (buildTime is not null && !RoslynString.IsNullOrEmpty(buildTime.Value)) - { - stringBuilder.Append(CultureInfo.InvariantCulture, $" (UTC {buildTime.Value})"); - } + if (_platformInformation.BuildDate is { } buildDate) + { + stringBuilder.Append(CultureInfo.InvariantCulture, $" (UTC {buildDate.UtcDateTime.ToShortDateString()})"); } - stringBuilder.Append(" ["); + if (_runtimeFeature.IsDynamicCodeSupported) + { + stringBuilder.Append(" ["); #if !NETCOREAPP - stringBuilder.Append(RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant()); + stringBuilder.Append(RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant()); #else - stringBuilder.Append(RuntimeInformation.RuntimeIdentifier); + stringBuilder.Append(RuntimeInformation.RuntimeIdentifier); #endif - stringBuilder.Append(CultureInfo.InvariantCulture, $" - {RuntimeInformation.FrameworkDescription}]"); - } + stringBuilder.Append(CultureInfo.InvariantCulture, $" - {RuntimeInformation.FrameworkDescription}]"); + } - _console.WriteLine(stringBuilder.ToString()); + _console.WriteLine(stringBuilder.ToString()); + } } if (_fileLoggerProvider is not null) @@ -187,7 +183,14 @@ public virtual async Task DisplayBannerAsync() try { _console.SetForegroundColor(ConsoleColor.Yellow); - _console.WriteLine($"Diagnostic file (level '{_fileLoggerProvider.LogLevel}' with {(_fileLoggerProvider.SyncFlush ? "sync flush" : "async flush")}): {_fileLoggerProvider.FileLogger.FileName}"); + if (_fileLoggerProvider.SyncFlush) + { + _console.WriteLine(string.Format(CultureInfo.CurrentCulture, PlatformResources.DiagnosticFileLevelWithFlush, _fileLoggerProvider.LogLevel, _fileLoggerProvider.FileLogger.FileName)); + } + else + { + _console.WriteLine(string.Format(CultureInfo.CurrentCulture, PlatformResources.DiagnosticFileLevelWithAsyncFlush, _fileLoggerProvider.LogLevel, _fileLoggerProvider.FileLogger.FileName)); + } } finally { @@ -240,16 +243,20 @@ public async Task DisplayAfterSessionEndRunAsync() if (!_firstCallTo_OnSessionStartingAsync) { - string passedOrFailedOrAborted = _totalFailedTests > 0 ? "Failed!" : "Passed!"; - passedOrFailedOrAborted = _totalTests == 0 ? "Zero tests ran" : passedOrFailedOrAborted; - passedOrFailedOrAborted = _testApplicationCancellationTokenSource.CancellationToken.IsCancellationRequested ? "Aborted" : passedOrFailedOrAborted; - passedOrFailedOrAborted = _totalTests < _minimumExpectedTest ? $"Minimum expected tests policy violation, tests ran {_totalTests}, minimum expected {_minimumExpectedTest}" : passedOrFailedOrAborted; + (bool isSuccess, string testRunResultMessage) = (_testApplicationCancellationTokenSource.CancellationToken.IsCancellationRequested, _totalTests, _totalFailedTests) switch + { + (true, _, _) => (false, PlatformResources.Aborted), + (false, _, _) when _totalTests < _minimumExpectedTest => (false, string.Format(CultureInfo.CurrentCulture, PlatformResources.MinimumExpectedTestsPolicyViolation, _totalTests, _minimumExpectedTest)), + (false, _, _) when _totalTests == 0 || _totalTests == _totalSkippedTests => (false, PlatformResources.ZeroTestsRan), + (false, _, > 0) => (false, string.Format(CultureInfo.CurrentCulture, "{0}!", PlatformResources.Failed)), + _ => (true, string.Format(CultureInfo.CurrentCulture, "{0}!", PlatformResources.Passed)), + }; ConsoleColor currentForeground = _console.GetForegroundColor(); - ConsoleColor consoleColor = passedOrFailedOrAborted == "Passed!" ? ConsoleColor.Green : ConsoleColor.Red; + ConsoleColor consoleColor = isSuccess ? ConsoleColor.Green : ConsoleColor.Red; try { _console.SetForegroundColor(consoleColor); - _console.WriteLine($"{passedOrFailedOrAborted} - Failed: {_totalFailedTests}, Passed: {_totalPassedTests}, Skipped: {_totalSkippedTests}, Total: {_totalTests}{(_testRequestExecutionTimeInfo is not null ? $", Duration: {ToHumanReadableDuration(_testRequestExecutionTimeInfo.Value.TimingInfo.Duration.TotalMilliseconds)}" : string.Empty)} - {Path.GetFileName(moduleOutput)} {(runtimeInformation is null ? string.Empty : $"({runtimeInformation})")}"); + _console.WriteLine($"{testRunResultMessage} - {PlatformResources.Failed}: {_totalFailedTests}, {PlatformResources.Passed}: {_totalPassedTests}, {PlatformResources.Skipped}: {_totalSkippedTests}, {PlatformResources.Total}: {_totalTests}{(_testRequestExecutionTimeInfo is not null ? $", Duration: {ToHumanReadableDuration(_testRequestExecutionTimeInfo.Value.TimingInfo.Duration.TotalMilliseconds)}" : string.Empty)} - {Path.GetFileName(moduleOutput)} {(runtimeInformation is null ? string.Empty : $"({runtimeInformation})")}"); } finally { @@ -269,10 +276,10 @@ public async Task DisplayAfterSessionEndRunAsync() StringBuilder artifacts = new(); bool hasArtifacts = false; - artifacts.AppendLine(CultureInfo.InvariantCulture, $"{(_firstCallTo_OnSessionStartingAsync ? "Out of process" : "In process")} file artifacts produced:"); + artifacts.AppendLine(_firstCallTo_OnSessionStartingAsync ? PlatformResources.OutOfProcessArtifactsProduced : PlatformResources.InProcessArtifactsProduced); foreach (TestNodeFileArtifact testNodeFileArtifact in _sessionFilesArtifact.OfType()) { - artifacts.AppendLine(CultureInfo.InvariantCulture, $"- For test {testNodeFileArtifact.TestNode.DisplayName}: {testNodeFileArtifact.FileInfo.FullName}"); + artifacts.AppendLine(CultureInfo.InvariantCulture, $"- {PlatformResources.ForTest} {testNodeFileArtifact.TestNode.DisplayName}: {testNodeFileArtifact.FileInfo.FullName}"); } foreach (SessionFileArtifact sessionFileArtifact in _sessionFilesArtifact.Except(_sessionFilesArtifact.OfType())) @@ -449,7 +456,7 @@ await HandleFailuresAsync( // For other run, skip displaying passed tests. if (_runtimeFeature.IsHotReloadEnabled) { - await ConsoleWriteAsync("passed", ConsoleColor.DarkGreen); + await ConsoleWriteAsync(PlatformResources.PassedLowercase, ConsoleColor.DarkGreen); await ConsoleWriteAsync($" {testNodeStateChanged.TestNode.DisplayName}"); await ConsoleWriteLineAsync($" {duration}", ConsoleColor.Gray); } @@ -459,7 +466,7 @@ await HandleFailuresAsync( break; case SkippedTestNodeStateProperty: - await ConsoleWriteAsync("skipped", ConsoleColor.Yellow); + await ConsoleWriteAsync(PlatformResources.SkippedLowercase, ConsoleColor.Yellow); await ConsoleWriteAsync($" {testNodeStateChanged.TestNode.DisplayName}"); await ConsoleWriteLineAsync($" {duration}", ConsoleColor.Gray); _totalTests++; @@ -484,10 +491,10 @@ await HandleFailuresAsync( protected virtual async Task HandleFailuresAsync(string testDisplayName, bool isCancelled, string? duration, string? errorMessage, string? errorStackTrace, string? expected, string? actual) { - await ConsoleWriteAsync("failed", ConsoleColor.DarkRed); + await ConsoleWriteAsync(PlatformResources.FailedLowercase, ConsoleColor.DarkRed); if (isCancelled) { - await ConsoleWriteAsync("(cancelled)", ConsoleColor.DarkRed); + await ConsoleWriteAsync($"({PlatformResources.CancelledLowercase})", ConsoleColor.DarkRed); } await ConsoleWriteAsync($" {testDisplayName}"); @@ -501,19 +508,19 @@ protected virtual async Task HandleFailuresAsync(string testDisplayName, bool is if (expected is not null) { - await ConsoleWriteLineAsync("Expected:", ConsoleColor.Red); + await ConsoleWriteLineAsync($"{PlatformResources.Expected}:", ConsoleColor.Red); await ConsoleWriteLineAsync(expected, ConsoleColor.Red); } if (actual is not null) { - await ConsoleWriteLineAsync("Actual:", ConsoleColor.Red); + await ConsoleWriteLineAsync($"{PlatformResources.Actual}:", ConsoleColor.Red); await ConsoleWriteLineAsync(actual, ConsoleColor.Red); } if (errorStackTrace != null) { - await ConsoleWriteLineAsync("Stack Trace:", ConsoleColor.DarkRed); + await ConsoleWriteLineAsync($"{PlatformResources.StackTrace}:", ConsoleColor.DarkRed); await ConsoleWriteLineAsync(errorStackTrace, ConsoleColor.DarkRed); } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/IColor.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/IColor.cs index e0bcf8b1fe..bfd6173af1 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/IColor.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/IColor.cs @@ -6,6 +6,4 @@ namespace Microsoft.Testing.Platform.OutputDevice; /// /// Represents a color. /// -public interface IColor -{ -} +public interface IColor; diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/IOutputDeviceData.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/IOutputDeviceData.cs index dd10c9e812..25f10cef39 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/IOutputDeviceData.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/IOutputDeviceData.cs @@ -6,6 +6,4 @@ namespace Microsoft.Testing.Platform.OutputDevice; /// /// Represents the data for an output device. /// -public interface IOutputDeviceData -{ -} +public interface IOutputDeviceData; diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/IOutputDeviceDataProducer.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/IOutputDeviceDataProducer.cs index e31ac4d210..5ca6965993 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/IOutputDeviceDataProducer.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/IOutputDeviceDataProducer.cs @@ -6,6 +6,4 @@ namespace Microsoft.Testing.Platform.Extensions.OutputDevice; /// /// Represents a producer of data that will be directed to the output device. /// -public interface IOutputDeviceDataProducer : IExtension -{ -} +public interface IOutputDeviceDataProducer : IExtension; diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/IPlatformOutputDevice.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/IPlatformOutputDevice.cs index f27ea61a42..ac87b96c2a 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/IPlatformOutputDevice.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/IPlatformOutputDevice.cs @@ -7,7 +7,7 @@ namespace Microsoft.Testing.Platform.OutputDevice; internal interface IPlatformOutputDevice : IExtension, IOutputDevice { - Task DisplayBannerAsync(); + Task DisplayBannerAsync(string? bannerMessage); Task DisplayBeforeSessionStartAsync(); diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs index 05cd69b93b..16fdcf3230 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs @@ -26,41 +26,26 @@ internal async Task BuildAsync(ServiceProvider servicePro IPlatformOutputDevice platformOutputDevice = _platformOutputDeviceFactory(serviceProvider); if (await platformOutputDevice.IsEnabledAsync()) { - if (platformOutputDevice is IAsyncInitializableExtension platformOutputDeviceAsync) - { - await platformOutputDeviceAsync.InitializeAsync(); - } + await platformOutputDevice.TryInitializeAsync(); return platformOutputDevice; } - else - { - return CreateDefault(); - } - } - else - { - return CreateDefault(); } - IPlatformOutputDevice CreateDefault() - { - ConsoleOutputDevice defaultConsoleOutputDevice = new( - serviceProvider.GetTestApplicationCancellationTokenSource(), - serviceProvider.GetConsole(), - serviceProvider.GetTestApplicationModuleInfo(), - serviceProvider.GetTestHostControllerInfo(), - serviceProvider.GetAsyncMonitorFactory().Create(), - serviceProvider.GetRuntimeFeature(), - serviceProvider.GetEnvironment(), - serviceProvider.GetProcessHandler(), - loggingState.CommandLineParseResult.IsOptionSet(PlatformCommandLineProvider.VSTestAdapterModeOptionKey), - loggingState.CommandLineParseResult.IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey), - loggingState.CommandLineParseResult.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey), - PlatformCommandLineProvider.GetMinimumExpectedTests(loggingState.CommandLineParseResult), - loggingState.FileLoggerProvider); - - return defaultConsoleOutputDevice; - } + return new ConsoleOutputDevice( + serviceProvider.GetTestApplicationCancellationTokenSource(), + serviceProvider.GetConsole(), + serviceProvider.GetTestApplicationModuleInfo(), + serviceProvider.GetTestHostControllerInfo(), + serviceProvider.GetAsyncMonitorFactory().Create(), + serviceProvider.GetRuntimeFeature(), + serviceProvider.GetEnvironment(), + serviceProvider.GetProcessHandler(), + serviceProvider.GetPlatformInformation(), + loggingState.CommandLineParseResult.IsOptionSet(PlatformCommandLineProvider.VSTestAdapterModeOptionKey), + loggingState.CommandLineParseResult.IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey), + loggingState.CommandLineParseResult.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey), + PlatformCommandLineProvider.GetMinimumExpectedTests(loggingState.CommandLineParseResult), + loggingState.FileLoggerProvider); } } diff --git a/src/Platform/Microsoft.Testing.Platform/PlatformVersion.cs.template b/src/Platform/Microsoft.Testing.Platform/PlatformVersion.cs.template index 5d1b0fd719..d943c277ab 100644 --- a/src/Platform/Microsoft.Testing.Platform/PlatformVersion.cs.template +++ b/src/Platform/Microsoft.Testing.Platform/PlatformVersion.cs.template @@ -1,5 +1,7 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +#pragma warning disable IDE0073 // The file header does not match the required text +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#pragma warning restore IDE0073 // The file header does not match the required text /// /// Repository version, created at build time. diff --git a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt index 7dc5c58110..f50559e437 100644 --- a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt @@ -1 +1,8 @@ #nullable enable +[TPEXP]Microsoft.Testing.Platform.Capabilities.TestFramework.IBannerMessageOwnerCapability +[TPEXP]Microsoft.Testing.Platform.Capabilities.TestFramework.IBannerMessageOwnerCapability.GetBannerMessageAsync() -> System.Threading.Tasks.Task! +[TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation +[TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation.BuildDate.get -> System.DateTimeOffset? +[TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation.CommitHash.get -> string? +[TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation.Name.get -> string! +[TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation.Version.get -> string? diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/ITestExecutionFilter.cs b/src/Platform/Microsoft.Testing.Platform/Requests/ITestExecutionFilter.cs index 2a47aeb59f..2073f78dcc 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/ITestExecutionFilter.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/ITestExecutionFilter.cs @@ -6,6 +6,4 @@ namespace Microsoft.Testing.Platform.Requests; /// /// Represents a filter for test execution. /// -public interface ITestExecutionFilter -{ -} +public interface ITestExecutionFilter; diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/NopFilter.cs b/src/Platform/Microsoft.Testing.Platform/Requests/NopFilter.cs index 922fd8bdbe..c8204a2493 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/NopFilter.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/NopFilter.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics.CodeAnalysis; + namespace Microsoft.Testing.Platform.Requests; -internal sealed class NopFilter : ITestExecutionFilter -{ -} +[Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] +[SuppressMessage("ApiDesign", "RS0016:Add public types and members to the declared API", Justification = "Experimental API")] +public sealed class NopFilter : ITestExecutionFilter; diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TestHostTestFrameworkInvoker.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TestHostTestFrameworkInvoker.cs index 6fe79d054d..c1c12d85fd 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TestHostTestFrameworkInvoker.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TestHostTestFrameworkInvoker.cs @@ -30,15 +30,15 @@ internal class TestHostTestFrameworkInvoker(IServiceProvider serviceProvider) : public string Description => string.Empty; - public Type[] DataTypesProduced => new[] { typeof(TestRequestExecutionTimeInfo) }; + public Type[] DataTypesProduced => [typeof(TestRequestExecutionTimeInfo)]; public Task IsEnabledAsync() => Task.FromResult(true); - public async Task ExecuteAsync(ITestFramework testFrameworkAdapter, ClientInfo client, CancellationToken cancellationToken) + public async Task ExecuteAsync(ITestFramework testFramework, ClientInfo client, CancellationToken cancellationToken) { ILogger logger = ServiceProvider.GetLoggerFactory().CreateLogger(); - await logger.LogInformationAsync($"TestFrameworkAdapter UID: '{testFrameworkAdapter.Uid}' Version: '{testFrameworkAdapter.Version}' DisplayName: '{testFrameworkAdapter.DisplayName}' Description: '{testFrameworkAdapter.Description}'"); + await logger.LogInformationAsync($"Test framework UID: '{testFramework.Uid}' Version: '{testFramework.Version}' DisplayName: '{testFramework.DisplayName}' Description: '{testFramework.Description}'"); foreach (ICapability capability in ServiceProvider.GetTestFrameworkCapabilities().Capabilities) { @@ -51,7 +51,7 @@ public async Task ExecuteAsync(ITestFramework testFrameworkAdapter, ClientInfo c DateTimeOffset startTime = DateTimeOffset.UtcNow; var stopwatch = Stopwatch.StartNew(); SessionUid sessionId = ServiceProvider.GetTestSessionContext().SessionId; - CreateTestSessionResult createTestSessionResult = await testFrameworkAdapter.CreateTestSessionAsync(new(sessionId, client, cancellationToken)); + CreateTestSessionResult createTestSessionResult = await testFramework.CreateTestSessionAsync(new(sessionId, client, cancellationToken)); await HandleTestSessionResultAsync(createTestSessionResult.IsSuccess, createTestSessionResult.WarningMessage, createTestSessionResult.ErrorMessage); ITestExecutionRequestFactory testExecutionRequestFactory = ServiceProvider.GetTestExecutionRequestFactory(); @@ -59,18 +59,18 @@ public async Task ExecuteAsync(ITestFramework testFrameworkAdapter, ClientInfo c IMessageBus messageBus = ServiceProvider.GetMessageBus(); // Execute the test request - await ExecuteRequestAsync(testFrameworkAdapter, request, messageBus, cancellationToken); + await ExecuteRequestAsync(testFramework, request, messageBus, cancellationToken); - CloseTestSessionResult closeTestSessionResult = await testFrameworkAdapter.CloseTestSessionAsync(new(sessionId, client, cancellationToken)); + CloseTestSessionResult closeTestSessionResult = await testFramework.CloseTestSessionAsync(new(sessionId, client, cancellationToken)); await HandleTestSessionResultAsync(closeTestSessionResult.IsSuccess, closeTestSessionResult.WarningMessage, closeTestSessionResult.ErrorMessage); DateTimeOffset endTime = DateTimeOffset.UtcNow; await messageBus.PublishAsync(this, new TestRequestExecutionTimeInfo(new TimingInfo(startTime, endTime, stopwatch.Elapsed))); } - public virtual async Task ExecuteRequestAsync(ITestFramework testFrameworkAdapter, TestExecutionRequest request, IMessageBus messageBus, CancellationToken cancellationToken) + public virtual async Task ExecuteRequestAsync(ITestFramework testFramework, TestExecutionRequest request, IMessageBus messageBus, CancellationToken cancellationToken) { using SemaphoreSlim requestSemaphore = new(0, 1); - await testFrameworkAdapter.ExecuteRequestAsync(new(request, messageBus, requestSemaphore, cancellationToken)); + await testFramework.ExecuteRequestAsync(new(request, messageBus, requestSemaphore, cancellationToken)); await requestSemaphore.WaitAsync(cancellationToken); } diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/FilterExpression.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/FilterExpression.cs index d5a6fd53f5..e23783ad70 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/FilterExpression.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/FilterExpression.cs @@ -3,6 +3,4 @@ namespace Microsoft.Testing.Platform.Requests; -internal abstract class FilterExpression -{ -} +internal abstract class FilterExpression; diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/NopExpression.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/NopExpression.cs index 34e2774f6f..102827761d 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/NopExpression.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/NopExpression.cs @@ -3,6 +3,4 @@ namespace Microsoft.Testing.Platform.Requests; -internal sealed class NopExpression : FilterExpression -{ -} +internal sealed class NopExpression : FilterExpression; diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs index 0959546940..8fb997b1d0 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text; using System.Text.RegularExpressions; @@ -11,7 +12,9 @@ namespace Microsoft.Testing.Platform.Requests; -internal sealed class TreeNodeFilter : ITestExecutionFilter +[Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] +[SuppressMessage("ApiDesign", "RS0016:Add public types and members to the declared API", Justification = "Experimental API")] +public sealed class TreeNodeFilter : ITestExecutionFilter { public const char PathSeparator = '/'; internal const char PropertyPerEdgeStartChar = '['; @@ -268,8 +271,8 @@ private static void ValidateExpression(FilterExpression expr, bool isMatchAllAll switch (expr) { case OperatorExpression { Op: FilterOperator.Not, SubExpressions: var subexprsNot } when subexprsNot.Count != 1: - case OperatorExpression { Op: FilterOperator.And, SubExpressions: var subexprsAnd } when subexprsAnd.Count < 2: - case OperatorExpression { Op: FilterOperator.Or, SubExpressions: var subexprsOr } when subexprsOr.Count < 2: + case OperatorExpression { Op: FilterOperator.And, SubExpressions.Count: < 2 }: + case OperatorExpression { Op: FilterOperator.Or, SubExpressions.Count: < 2 }: throw ApplicationStateGuard.Unreachable(); case OperatorExpression opExpr: @@ -294,11 +297,11 @@ private static void ProcessStackOperator(OperatorKind op, Stack subexprs = new() - { - expr.Pop(), + List subexprs = + [ expr.Pop(), - }; + expr.Pop() + ]; // Note: An OR/AND operator allow to pass it in a list of expressions. // We can keep popping following operators and add them to the collection, diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.Designer.cs b/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.Designer.cs index 665c8f9ba8..3ea554ab71 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.Designer.cs +++ b/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.Designer.cs @@ -60,6 +60,42 @@ internal PlatformResources() { } } + /// + /// Looks up a localized string similar to Aborted. + /// + internal static string Aborted { + get { + return ResourceManager.GetString("Aborted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Actual. + /// + internal static string Actual { + get { + return ResourceManager.GetString("Actual", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to cancelled. + /// + internal static string CancelledLowercase { + get { + return ResourceManager.GetString("CancelledLowercase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cancelling the test session.... + /// + internal static string CancellingTestSession { + get { + return ResourceManager.GetString("CancellingTestSession", resourceCulture); + } + } + /// /// Looks up a localized string similar to Failed to create a test execution filter. /// @@ -321,6 +357,24 @@ internal static string CouldNotFindDirectoryErrorMessage { } } + /// + /// Looks up a localized string similar to Diagnostic file (level '{0}' with async flush): {1}". + /// + internal static string DiagnosticFileLevelWithAsyncFlush { + get { + return ResourceManager.GetString("DiagnosticFileLevelWithAsyncFlush", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Diagnostic file (level '{0}' with sync flush): {1}". + /// + internal static string DiagnosticFileLevelWithFlush { + get { + return ResourceManager.GetString("DiagnosticFileLevelWithFlush", resourceCulture); + } + } + /// /// Looks up a localized string similar to Provider '{0}' (UID: {1}) failed with error: {2}. /// @@ -330,6 +384,15 @@ internal static string EnvironmentVariableProviderFailedWithError { } } + /// + /// Looks up a localized string similar to Expected. + /// + internal static string Expected { + get { + return ResourceManager.GetString("Expected", resourceCulture); + } + } + /// /// Looks up a localized string similar to Extension of type '{0}' is not implementing the required '{1}' interface. /// @@ -348,6 +411,24 @@ internal static string ExtensionWithSameUidAlreadyRegisteredErrorMessage { } } + /// + /// Looks up a localized string similar to Failed. + /// + internal static string Failed { + get { + return ResourceManager.GetString("Failed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to failed. + /// + internal static string FailedLowercase { + get { + return ResourceManager.GetString("FailedLowercase", resourceCulture); + } + } + /// /// Looks up a localized string similar to Failed to write the log to the channel. Missed log content: ///{0}. @@ -358,6 +439,15 @@ internal static string FailedToWriteLogToChannelErrorMessage { } } + /// + /// Looks up a localized string similar to For test. + /// + internal static string ForTest { + get { + return ResourceManager.GetString("ForTest", resourceCulture); + } + } + /// /// Looks up a localized string similar to The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup:. /// @@ -421,6 +511,15 @@ internal static string HelpTestApplicationRunner { } } + /// + /// Looks up a localized string similar to In process file artifacts produced:. + /// + internal static string InProcessArtifactsProduced { + get { + return ResourceManager.GetString("InProcessArtifactsProduced", resourceCulture); + } + } + /// /// Looks up a localized string similar to Method '{0}' did not exit successfully. /// @@ -484,6 +583,15 @@ internal static string JsonRpcTcpServerToSingleClientDescription { } } + /// + /// Looks up a localized string similar to Minimum expected tests policy violation, tests ran {0}, minimum expected {1}. + /// + internal static string MinimumExpectedTestsPolicyViolation { + get { + return ResourceManager.GetString("MinimumExpectedTestsPolicyViolation", resourceCulture); + } + } + /// /// Looks up a localized string similar to No serializer registered with ID '{0}'. /// @@ -521,47 +629,56 @@ internal static string NotFound { } /// - /// Looks up a localized string similar to Specify the hostname of the client.. + /// Looks up a localized string similar to Out of process file artifacts produced:. /// - internal static string PlatformCommandLineClientHostOptionDescription { + internal static string OutOfProcessArtifactsProduced { get { - return ResourceManager.GetString("PlatformCommandLineClientHostOptionDescription", resourceCulture); + return ResourceManager.GetString("OutOfProcessArtifactsProduced", resourceCulture); } } /// - /// Looks up a localized string similar to '--client-host' expects a single host name as argument. + /// Looks up a localized string similar to Passed. /// - internal static string PlatformCommandLineClientHostOptionSingleArgument { + internal static string Passed { get { - return ResourceManager.GetString("PlatformCommandLineClientHostOptionSingleArgument", resourceCulture); + return ResourceManager.GetString("Passed", resourceCulture); } } /// - /// Looks up a localized string similar to Specify the port of the client.. + /// Looks up a localized string similar to passed. /// - internal static string PlatformCommandLineClientPortOptionDescription { + internal static string PassedLowercase { get { - return ResourceManager.GetString("PlatformCommandLineClientPortOptionDescription", resourceCulture); + return ResourceManager.GetString("PassedLowercase", resourceCulture); } } /// - /// Looks up a localized string similar to Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). Note that this is slowing down the test execution.. + /// Looks up a localized string similar to Specify the hostname of the client.. /// - internal static string PlatformCommandLineDiagnosticFileLoggerSynchronousWriteOptionDescription { + internal static string PlatformCommandLineClientHostOptionDescription { get { - return ResourceManager.GetString("PlatformCommandLineDiagnosticFileLoggerSynchronousWriteOptionDescription", resourceCulture); + return ResourceManager.GetString("PlatformCommandLineClientHostOptionDescription", resourceCulture); } } /// - /// Looks up a localized string similar to '--diagnostic-output-fileprefix' expects a single file name prefix argument. + /// Looks up a localized string similar to Specify the port of the client.. + /// + internal static string PlatformCommandLineClientPortOptionDescription { + get { + return ResourceManager.GetString("PlatformCommandLineClientPortOptionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). Note that this is slowing down the test execution.. /// - internal static string PlatformCommandLineDiagnosticFilePrefixOptionSingleArgument { + internal static string PlatformCommandLineDiagnosticFileLoggerSynchronousWriteOptionDescription { get { - return ResourceManager.GetString("PlatformCommandLineDiagnosticFilePrefixOptionSingleArgument", resourceCulture); + return ResourceManager.GetString("PlatformCommandLineDiagnosticFileLoggerSynchronousWriteOptionDescription", resourceCulture); } } @@ -601,15 +718,6 @@ internal static string PlatformCommandLineDiagnosticOutputDirectoryOptionDescrip } } - /// - /// Looks up a localized string similar to '--diagnostic-output-directory' expects a single directory name argument. - /// - internal static string PlatformCommandLineDiagnosticOutputDirectoryOptionSingleArgument { - get { - return ResourceManager.GetString("PlatformCommandLineDiagnosticOutputDirectoryOptionSingleArgument", resourceCulture); - } - } - /// /// Looks up a localized string similar to Prefix for the log file name that will replace '[log]_.'. /// @@ -637,13 +745,22 @@ internal static string PlatformCommandLineDiscoverTestsOptionDescription { } } + /// + /// Looks up a localized string similar to dotnet test pipe.. + /// + internal static string PlatformCommandLineDotnetTestPipe { + get { + return ResourceManager.GetString("PlatformCommandLineDotnetTestPipe", resourceCulture); + } + } + /// /// Looks up a localized string similar to Invalid PID '{0}' ///{1}. /// - internal static string PlatformCommandLineExitOnProcessExitInvalidDependantProcess { + internal static string PlatformCommandLineExitOnProcessExitInvalidDependentProcess { get { - return ResourceManager.GetString("PlatformCommandLineExitOnProcessExitInvalidDependantProcess", resourceCulture); + return ResourceManager.GetString("PlatformCommandLineExitOnProcessExitInvalidDependentProcess", resourceCulture); } } @@ -719,15 +836,6 @@ internal static string PlatformCommandLineNoBannerOptionDescription { } } - /// - /// Looks up a localized string similar to '--{0}' expects no argument. - /// - internal static string PlatformCommandLineOptionExpectsNoArgumentErrorMessage { - get { - return ResourceManager.GetString("PlatformCommandLineOptionExpectsNoArgumentErrorMessage", resourceCulture); - } - } - /// /// Looks up a localized string similar to Specify the port of the server.. /// @@ -890,6 +998,33 @@ internal static string ServiceProviderShouldNotRegisterTestFramework { } } + /// + /// Looks up a localized string similar to Skipped. + /// + internal static string Skipped { + get { + return ResourceManager.GetString("Skipped", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to skipped. + /// + internal static string SkippedLowercase { + get { + return ResourceManager.GetString("SkippedLowercase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stack Trace. + /// + internal static string StackTrace { + get { + return ResourceManager.GetString("StackTrace", resourceCulture); + } + } + /// /// Looks up a localized string similar to Starting server. Listening on port '{0}'. /// @@ -1048,6 +1183,15 @@ internal static string TimeoutFlushingLogsErrorMessage { } } + /// + /// Looks up a localized string similar to Total. + /// + internal static string Total { + get { + return ResourceManager.GetString("Total", resourceCulture); + } + } + /// /// Looks up a localized string similar to A filter '{0}' should not contain a '/' character. /// @@ -1075,15 +1219,6 @@ internal static string TreeNodeFilterEscapeCharacterShouldNotBeLastErrorMessage } } - /// - /// Looks up a localized string similar to A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]'). - /// - internal static string TreeNodeFilterInvalidArgumentCount { - get { - return ResourceManager.GetString("TreeNodeFilterInvalidArgumentCount", resourceCulture); - } - } - /// /// Looks up a localized string similar to Only the final filter path can contain '**' wildcard. /// @@ -1183,5 +1318,14 @@ internal static string UnreachableLocationErrorMessage { return ResourceManager.GetString("UnreachableLocationErrorMessage", resourceCulture); } } + + /// + /// Looks up a localized string similar to Zero tests ran. + /// + internal static string ZeroTestsRan { + get { + return ResourceManager.GetString("ZeroTestsRan", resourceCulture); + } + } } } diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx b/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx index 0f8215ff6c..991a66200d 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx +++ b/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx @@ -355,18 +355,12 @@ Specify the hostname of the client. - - '--client-host' expects a single host name as argument - Specify the port of the client. Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). Note that this is slowing down the test execution. - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag @@ -379,9 +373,6 @@ Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory. - - '--diagnostic-output-directory' expects a single directory name argument - Prefix for the log file name that will replace '[log]_.' @@ -406,9 +397,6 @@ Do not display the startup banner, the copyright message or the telemetry banner. - - '--{0}' expects no argument - Specify the port of the server. @@ -433,15 +421,15 @@ Eventual parent test host controller PID. + + dotnet test pipe. + Bridge to VSTest APIs Use a tree filter to filter down the tests to execute - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - Connecting to client host '{0}' port '{1}' @@ -495,8 +483,74 @@ Read more about Microsoft Testing Platform telemetry: https://aka.ms/testingplat '--{0}' expects a single int PID argument - + Invalid PID '{0}' {1} - \ No newline at end of file + + Aborted + + + Actual + + + cancelled + + + Cancelling the test session... + + + Diagnostic file (level '{0}' with async flush): {1}" + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + 0 level such as verbose, +1 path to file + + + Expected + + + Failed + + + failed + + + For test + is followed by test name + + + In process file artifacts produced: + + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + {0}, {1} number of tests + + + Out of process file artifacts produced: + + + Passed + + + passed + + + Skipped + + + skipped + + + Stack Trace + + + Total + + + Zero tests ran + + diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf index eb07bb307f..43c4e7bfeb 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf @@ -2,6 +2,26 @@ + + Aborted + Přerušeno + + + + Actual + Skutečnost + + + + cancelled + zrušeno + + + + Cancelling the test session... + Ruší se testovací relace… + + Failed to create a test execution filter Nepovedlo se vytvořit filtr provádění testů. @@ -147,11 +167,28 @@ Nepodařilo se najít adresář {0}. + + Diagnostic file (level '{0}' with async flush): {1}" + Diagnostický soubor (úroveň {0} s asynchronním vyprázdněním): {1} + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + Diagnostický soubor (úroveň {0} se synchronním vyprázdněním): {1} + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} Zprostředkovatel {0} (UID: {1}) selhal s chybou: {2}. + + Expected + Očekáváno + + Extension of type '{0}' is not implementing the required '{1}' interface Rozšíření typu {0} neimplementuje požadované rozhraní {1}. @@ -162,6 +199,16 @@ Už je zaregistrované jiné rozšíření se stejným UID {0}. Zaregistrované rozšíření je typu {1}. + + Failed + Neúspěšné + + + + failed + selhalo + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + Pro testování + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: Následující zprostředkovatelé ITestHostEnvironmentVariableProvider odmítli konečné nastavení proměnných prostředí: @@ -204,6 +256,11 @@ <spouštěč testovací aplikace> + + In process file artifacts produced: + Vytvořené artefakty souboru v procesu: + + Method '{0}' did not exit successfully Metoda {0} nebyla úspěšně ukončena. @@ -239,6 +296,11 @@ Handshake mezi klientem a serverem JsonRpc, implementace na základě specifikace protokolu testovací platformy + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + Minimální očekávané porušení zásad testů, spuštěné testy: {0}, očekávané minimum: {1} + {0}, {1} number of tests + No serializer registered with ID '{0}' Není zaregistrovaný žádný serializátor s ID {0}. @@ -259,16 +321,26 @@ Nenalezeno + + Out of process file artifacts produced: + Vytvořené artefakty souboru mimo proces: + + + + Passed + Úspěšné + + + + passed + úspěch + + Specify the hostname of the client. Zadejte název hostitele klienta. - - '--client-host' expects a single host name as argument - --client-host očekává jako argument jeden název hostitele. - - Specify the port of the client. Zadejte port klienta. @@ -279,11 +351,6 @@ Umožňuje vynutit synchronní zápis do protokolu integrovaným protokolovacím nástrojem souborů. Užitečné pro scénář, kdy nechcete přijít o žádný protokol (například v případě chybového ukončení). Poznámka: Toto zpomaluje provádění testu. - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - --diagnostic-output-fileprefix očekává jeden argument předpony názvu souboru. - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag Umožňuje povolit protokolování diagnostiky. Výchozí úroveň protokolování je Trasování. Soubor se zapíše do výstupního adresáře s názvem log_[MMddHHssfff].diag. @@ -304,11 +371,6 @@ Výstupní adresář diagnostického protokolování. Pokud není zadaný, soubor se vygeneruje ve výchozím adresáři TestResults. - - '--diagnostic-output-directory' expects a single directory name argument - --diagnostic-output-directory očekává jeden argument názvu adresáře. - - Prefix for the log file name that will replace '[log]_.' Předpona pro název souboru protokolu, který nahradí [log]_ @@ -324,7 +386,12 @@ Umožňuje zobrazit seznam dostupných testů. - + + dotnet test pipe. + testovací kanál dotnet. + + + Invalid PID '{0}' {1} Neplatný identifikátor PID „{0}“ @@ -371,11 +438,6 @@ Nezobrazovat úvodní banner, zprávu o autorských právech ani banner telemetrie - - '--{0}' expects no argument - --{0} neočekává žádný argument. - - Specify the port of the server. Zadejte port serveru. @@ -466,6 +528,21 @@ Instance typu ITestFramework by neměly být registrovány prostřednictvím poskytovatele služeb, ale prostřednictvím metody ITestApplicationBuilder.RegisterTestFramework. + + Skipped + Přeskočeno + + + + skipped + vynecháno + + + + Stack Trace + Trasování zásobníku + + Starting server. Listening on port '{0}' Spouští se server. Naslouchání na portu {0} @@ -561,6 +638,11 @@ Přečtěte si další informace o telemetrii Microsoft Testing Platform: https: Nepovedlo se vyprázdnit protokoly před vypršením časového limitu {0} s. + + Total + Celkem + + A filter '{0}' should not contain a '/' character Filtr {0} nesmí obsahovat znak /. @@ -576,11 +658,6 @@ Přečtěte si další informace o telemetrii Microsoft Testing Platform: https: Řetězec filtru by neměl být ukončen řídicím znakem {0}. - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - Očekává se jeden argument (například /MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]). - - Only the final filter path can contain '**' wildcard Zástupný znak ** může obsahovat pouze konečná cesta filtru. @@ -638,6 +715,11 @@ Přečtěte si další informace o telemetrii Microsoft Testing Platform: https: Toto umístění programu se považuje za nedostupné. File={0}, Line={1} + + Zero tests ran + Spustila se nula testů + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf index 270e89782e..2061108fe2 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf @@ -2,6 +2,26 @@ + + Aborted + Abgebrochen + + + + Actual + Aktuell + + + + cancelled + abgebrochen + + + + Cancelling the test session... + Testsitzung wird abgebrochen... + + Failed to create a test execution filter Fehler beim Erstellen eines Testausführungsfilters @@ -147,11 +167,28 @@ Das Verzeichnis "{0}" konnte nicht gefunden werden + + Diagnostic file (level '{0}' with async flush): {1}" + Diagnosedatei (Ebene "{0}" mit asynchroner Leerung): "{1}" + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + Diagnosedatei (Ebene "{0}" mit synchroner Leerung): "{1}" + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} Anbieter "{0}" (UID: {1}) ist mit folgendem Fehler fehlgeschlagen: {2} + + Expected + Erwartet + + Extension of type '{0}' is not implementing the required '{1}' interface Die Erweiterung vom Typ "{0}" implementiert nicht die erforderliche Schnittstelle "{1}" @@ -162,6 +199,16 @@ Eine andere Erweiterung mit derselben UID "{0}" wurde bereits registriert. Die registrierte Erweiterung ist vom Typ "{1}" + + Failed + Fehler + + + + failed + fehlerhaft + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + Für Test + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: Die folgenden Anbieter von "ITestHostEnvironmentVariableProvider" haben das endgültige Setup der Umgebungsvariablen abgelehnt: @@ -204,6 +256,11 @@ <test application runner> + + In process file artifacts produced: + In Bearbeitung Dateiartefakte erstellt: + + Method '{0}' did not exit successfully Die Methode "{0}" wurde nicht erfolgreich beendet @@ -239,6 +296,11 @@ JsonRpc-Server-zu-Client-Handshake, Implementierungen basierend auf der Protokollspezifikation der Testplattform. + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + Mindestens erwartete Testrichtlinienverletzung, {0} Tests wurden ausgeführt, mindestens erwartet: {1} + {0}, {1} number of tests + No serializer registered with ID '{0}' Es ist kein Serialisierungsmodul mit der ID "{0}" registriert @@ -259,16 +321,26 @@ Nicht gefunden + + Out of process file artifacts produced: + Nicht verarbeitete Dateiartefakte erstellt: + + + + Passed + Bestanden + + + + passed + erfolgreich + + Specify the hostname of the client. Geben Sie den Hostnamen des Clients an. - - '--client-host' expects a single host name as argument - "--client-host" erwartet einen einzelnen Hostnamen als Argument. - - Specify the port of the client. Geben Sie den Port des Clients an. @@ -279,11 +351,6 @@ Erzwingen Sie, dass die integrierte Dateiprotokollierung das Protokoll synchron schreibt. Nützlich für Szenarien, in denen Sie keine Protokolle verlieren möchten (z. B. im Fall eines Absturzes). Beachten Sie, dass die Testausführung dadurch verlangsamt wird. - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - "--diagnostic-output-fileprefix" erwartet ein einzelnes Präfixargument für Dateinamen. - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag Aktivieren Sie die Diagnoseprotokollierung. Die Standardprotokollebene ist "Ablaufverfolgung". Die Datei wird im Ausgabeverzeichnis mit dem Namen log_[MMddHHSSFFF].diag geschrieben @@ -304,11 +371,6 @@ Das Ausgabeverzeichnis der Diagnoseprotokollierung, sofern nicht angegeben, wird die Datei im Standardverzeichnis "TestResults" generiert. - - '--diagnostic-output-directory' expects a single directory name argument - "--diagnostic-output-directory" erwartet ein einzelnes Verzeichnisnamenargument. - - Prefix for the log file name that will replace '[log]_.' Präfix für den Protokolldateinamen, durch den "[log]_" ersetzt wird. @@ -324,7 +386,12 @@ Listen Sie verfügbare Tests auf. - + + dotnet test pipe. + dotnet-Testpipe. + + + Invalid PID '{0}' {1} Ungültige PID "{0}" @@ -371,11 +438,6 @@ Zeigen Sie das Startbanner, die Copyrightmeldung oder das Telemetriebanner nicht an. - - '--{0}' expects no argument - "--{0}" erwartet kein Argument - - Specify the port of the server. Geben Sie den Port des Servers an. @@ -466,6 +528,21 @@ Instanzen vom Typ "ITestFramework" sollten nicht über den Dienstanbieter, sondern über "ITestApplicationBuilder.RegisterTestFramework" registriert werden. + + Skipped + Übersprungen + + + + skipped + übersprungen + + + + Stack Trace + Stapelüberwachung + + Starting server. Listening on port '{0}' Server wird gestartet. An Port "{0}" lauschen @@ -561,6 +638,11 @@ Weitere Informationen zu Microsoft Testing Platform-Telemetriedaten: https://aka Fehler beim Leeren von Protokollen vor dem Timeout von "{0}" Sekunden + + Total + Gesamt + + A filter '{0}' should not contain a '/' character Ein Filter "{0}" darf kein "/"-Zeichen enthalten @@ -576,11 +658,6 @@ Weitere Informationen zu Microsoft Testing Platform-Telemetriedaten: https://aka Ein Escapezeichen darf die Filterzeichenfolge "{0}" nicht beenden - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - Es wird ein einzelnes Argument erwartet (z. B. "/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]"). - - Only the final filter path can contain '**' wildcard Nur der endgültige Filterpfad darf den Platzhalter "**" enthalten @@ -638,6 +715,11 @@ Weitere Informationen zu Microsoft Testing Platform-Telemetriedaten: https://aka Dieser Programmspeicherort wird als nicht erreichbar betrachtet. File='{0}' Line={1} + + Zero tests ran + Es wurden keine Tests ausgeführt. + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf index 7975207755..1e364b2c8a 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf @@ -2,6 +2,26 @@ + + Aborted + Anulado + + + + Actual + Real + + + + cancelled + cancelada + + + + Cancelling the test session... + Cancelando la sesión de prueba... + + Failed to create a test execution filter No se pudo crear un filtro de ejecución de pruebas @@ -147,11 +167,28 @@ No se pudo encontrar el directorio '{0}' + + Diagnostic file (level '{0}' with async flush): {1}" + Archivo de diagnóstico (nivel '{0}' con vaciado asincrónico): {1}" + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + Archivo de diagnóstico (nivel '{0}' con vaciado sincrónico): {1}" + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} Error del proveedor "{0}" (UID: {1}) con el error: {2} + + Expected + Se esperaba + + Extension of type '{0}' is not implementing the required '{1}' interface La extensión de tipo "{0}" no está implementando la interfaz "{1}" necesaria @@ -162,6 +199,16 @@ Ya se registró otra extensión con el mismo UID "{0}". La extensión registrada es de tipo "{1}" + + Failed + Error + + + + failed + con errores + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + Para prueba + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: Los siguientes proveedores "ITestHostEnvironmentVariableProvider" rechazaron la configuración final de las variables de entorno: @@ -204,6 +256,11 @@ <test application runner> + + In process file artifacts produced: + Artefactos de archivo en proceso producidos: + + Method '{0}' did not exit successfully El método '{0}' no se cerró correctamente @@ -239,6 +296,11 @@ Implementación del protocolo de enlace de servidor a cliente JsonRpc basada en la especificación del protocolo de la plataforma de pruebas. + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + Infracción de directiva de pruebas mínimas esperadas, pruebas ejecutadas {0}, mínimas esperadas {1} + {0}, {1} number of tests + No serializer registered with ID '{0}' No hay ningún serializador registrado con el id. '{0}' @@ -259,16 +321,26 @@ No se encontró + + Out of process file artifacts produced: + Artefactos de archivo fuera de proceso producidos: + + + + Passed + Correcta + + + + passed + correcto + + Specify the hostname of the client. Especifique el nombre de host del cliente. - - '--client-host' expects a single host name as argument - “--client-host” espera un único nombre de host como argumento - - Specify the port of the client. Especifique el puerto del cliente. @@ -279,11 +351,6 @@ Fuerce al registrador de archivos integrado a escribir el registro de forma sincrónica. Resulta útil para escenarios en los que no quiere perder ningún registro (es decir, en caso de bloqueo). Tenga en cuenta que esto ralentiza la ejecución de pruebas. - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - “--diagnostic-output-fileprefix” espera un único argumento de prefijo de nombre de archivo - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag Habilite el registro de diagnóstico. El nivel de registro predeterminado es "Seguimiento". El archivo se escribirá en el directorio de salida con el nombre log_[MMddHHssfff].diag @@ -304,11 +371,6 @@ Directorio de salida del registro de diagnóstico; si no se especifica, el archivo se generará dentro del directorio predeterminado “TestResults”. - - '--diagnostic-output-directory' expects a single directory name argument - “--diagnostic-output-directory” espera un único argumento de nombre de directorio - - Prefix for the log file name that will replace '[log]_.' Prefijo del nombre del archivo de registro que reemplazará a '[log]_.' @@ -324,7 +386,12 @@ Enumere las pruebas disponibles. - + + dotnet test pipe. + canalización de prueba de dotnet. + + + Invalid PID '{0}' {1} PID inválido '{0}' @@ -371,11 +438,6 @@ No muestre el banner de inicio, el mensaje de copyright o el banner de telemetría. - - '--{0}' expects no argument - “--{0}” no espera ningún argumento - - Specify the port of the server. Especifique el puerto del servidor. @@ -466,6 +528,21 @@ Las instancias de tipo "ITestFramework" no deben registrarse mediante el proveedor de servicios, sino mediante "ITestApplicationBuilder.RegisterTestFramework". + + Skipped + Omitida + + + + skipped + omitido + + + + Stack Trace + Seguimiento de la pila + + Starting server. Listening on port '{0}' Iniciando el servidor. Escuchando en el puerto '{0}' @@ -561,6 +638,11 @@ Más información sobre la telemetría de la Plataforma de pruebas de Microsoft: No se pudieron vaciar los registros antes del tiempo de espera de “{0}” segundos + + Total + Total + + A filter '{0}' should not contain a '/' character Un filtro "{0}" no debe contener un carácter "/". @@ -576,11 +658,6 @@ Más información sobre la telemetría de la Plataforma de pruebas de Microsoft: Un carácter de escape no debe terminar la cadena de filtro "{0}" - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - Se espera un único argumento (por ejemplo, “/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]”) - - Only the final filter path can contain '**' wildcard Solo la ruta de acceso de filtro final puede contener el carácter comodín "**". @@ -638,6 +715,11 @@ Más información sobre la telemetría de la Plataforma de pruebas de Microsoft: Se considera que esta ubicación del programa es inaccesible. Archivo=''{0}'' Línea={1} + + Zero tests ran + No se ejecutaron pruebas + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf index c428ec0fbc..dfd238a654 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf @@ -2,6 +2,26 @@ + + Aborted + Abandonné + + + + Actual + Réel + + + + cancelled + annulé + + + + Cancelling the test session... + Annulation de la session de test... + + Failed to create a test execution filter Désolé, échec de la création d’un filtre d’exécution de test @@ -147,11 +167,28 @@ Répertoire « {0} » introuvable. + + Diagnostic file (level '{0}' with async flush): {1}" + Fichier de diagnostic (niveau « {0} » avec vidage asynchrone) : {1} » + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + Fichier de diagnostic (niveau « {0} » avec vidage synchrone) : {1} » + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} Désolé, échec de l’« {0} » du fournisseur (UID : {1}) avec l’erreur : {2} + + Expected + Attendu + + Extension of type '{0}' is not implementing the required '{1}' interface Désolé, l’extension de type « {0} » n’implémente pas l’interface de « {1} » requise @@ -162,6 +199,16 @@ Désolé, une autre extension avec le même UID « {0} » a déjà été inscrite. Désolé, l’extension inscrite est de type « {1} » + + Failed + Échec + + + + failed + échec + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + Pour le test + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: Les fournisseurs « ITestHostEnvironmentVariableProvider » suivants ont rejeté la configuration finale des variables d’environnement : @@ -204,6 +256,11 @@ <test application runner> + + In process file artifacts produced: + Artéfacts produits dans les dossiers en cours de traitement : + + Method '{0}' did not exit successfully La méthode « {0} » ne s’est pas arrêtée correctement @@ -239,6 +296,11 @@ Établissement d’une liaison entre le serveur et le client JsonRpc, implémentation basée sur la spécification du protocole de la plateforme de test. + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + Violation de stratégie de tests minimale attendue, tests exécutés {0}, minimum attendu {1} + {0}, {1} number of tests + No serializer registered with ID '{0}' Aucun sérialiseur inscrit avec l’ID « {0} » @@ -259,16 +321,26 @@ Introuvable + + Out of process file artifacts produced: + Artefacts de fichier hors processus produits : + + + + Passed + Réussite + + + + passed + réussite + + Specify the hostname of the client. Spécifier le nom d’hôte du client. - - '--client-host' expects a single host name as argument - « --client-host » attend un nom d’hôte unique comme argument - - Specify the port of the client. Spécifier le port du client. @@ -279,11 +351,6 @@ Forcer l’enregistreur d’événements de fichiers intégré à écrire le journal de manière synchrone. Utile pour les scénarios où vous ne voulez pas perdre de journal (c’est-à-dire en cas d’incident). Notez que cela ralentit l’exécution du test. - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - « --diagnostic-output-fileprefix » attend un argument de préfixe de nom de fichier unique - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag Activez la journalisation des diagnostics. Le niveau de consignation par défaut est « Trace ». Le fichier sera écrit dans le répertoire de sortie avec le nom log_[MMddHHssfff].diag @@ -304,11 +371,6 @@ Répertoire de sortie de la journalisation des diagnostics. S’il n’est pas spécifié, le fichier est généré dans le répertoire ’TestResults’ par défaut. - - '--diagnostic-output-directory' expects a single directory name argument - « --diagnostic-output-directory » attend un argument de nom de répertoire unique - - Prefix for the log file name that will replace '[log]_.' Préfixe du nom du fichier journal qui remplacera « [log]_ ». @@ -324,7 +386,12 @@ Répertorier les tests disponibles. - + + dotnet test pipe. + canal de test dotnet. + + + Invalid PID '{0}' {1} PID incorrect « {0} » @@ -371,11 +438,6 @@ Ne pas afficher la bannière de démarrage, le message de copyright ou la bannière de télémétrie. - - '--{0}' expects no argument - « --{0} » n’attend aucun argument - - Specify the port of the server. Spécifier le port du serveur. @@ -466,6 +528,21 @@ Les instances de type « ITestFramework » ne doivent pas être inscrites par le biais du fournisseur de services, mais par le biais de « ITestApplicationBuilder.RegisterTestFramework » + + Skipped + Ignoré + + + + skipped + ignoré + + + + Stack Trace + Rapport des appels de procédure + + Starting server. Listening on port '{0}' Démarrage du serveur. Écoute sur le port « {0} » @@ -561,6 +638,11 @@ En savoir plus sur la télémétrie de la plateforme de tests Microsoft : https: Échec du vidage des journaux avant le délai d’expiration de « {0} » secondes + + Total + Total + + A filter '{0}' should not contain a '/' character Un filtre « {0} » ne doit pas contenir de caractère '/' @@ -576,11 +658,6 @@ En savoir plus sur la télémétrie de la plateforme de tests Microsoft : https: Désolé, un caractère d’échappement ne doit pas terminer la chaîne de filtre « {0} » - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - Un seul argument est attendu (par ex., « /MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux] ») - - Only the final filter path can contain '**' wildcard Désolé, seul le chemin d’accès de filtre final peut contenir le caractère générique ’**’ @@ -638,6 +715,11 @@ En savoir plus sur la télémétrie de la plateforme de tests Microsoft : https: Cet emplacement du programme est considéré comme inaccessible. File=« {0} », Ligne={1} + + Zero tests ran + Zéro tests exécutés + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf index 602e318c3f..33ae55f983 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf @@ -2,6 +2,26 @@ + + Aborted + Operazione interrotta + + + + Actual + Effettivi + + + + cancelled + annullata + + + + Cancelling the test session... + Annullamento della sessione di test in corso... + + Failed to create a test execution filter Non è stato possibile creare un filtro di esecuzione dei test @@ -147,11 +167,28 @@ Non è stato possibile trovare la directory '{0}' + + Diagnostic file (level '{0}' with async flush): {1}" + File di diagnostica (livello '{0}' con scaricamento asincrono): {1}" + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + File di diagnostica (livello '{0}' con scaricamento sincrono): {1}" + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} Provider '{0}' (UID: {1}) non riuscito con errore: {2} + + Expected + Previsto + + Extension of type '{0}' is not implementing the required '{1}' interface L'estensione di tipo '{0}' non implementa l'interfaccia '{1}' richiesta @@ -162,6 +199,16 @@ È già stata registrata un'altra estensione con lo stesso UID '{0}'. L'estensione registrata è di tipo '{1}' + + Failed + Errore + + + + failed + operazione non riuscita + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + Per test + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: I provider 'ITestHostEnvironmentVariableProvider' seguenti hanno rifiutato l'installazione finale delle variabili di ambiente: @@ -204,6 +256,11 @@ <test application runner> + + In process file artifacts produced: + Artefatti file in fase di elaborazione prodotti: + + Method '{0}' did not exit successfully Il metodo '{0}' non è stato chiuso correttamente @@ -239,6 +296,11 @@ Handshake da server JsonRpc a client, implementazione basata sulla specifica del protocollo della piattaforma di test. + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + Violazione minima dei criteri di test previsti, test eseguiti {0}, minimo previsto {1} + {0}, {1} number of tests + No serializer registered with ID '{0}' Nessun serializzatore registrato con ID '{0}' @@ -259,16 +321,26 @@ Non trovato + + Out of process file artifacts produced: + Artefatti file non in elaborazione prodotti: + + + + Passed + Superato + + + + passed + superato + + Specify the hostname of the client. Specifica il nome host del client. - - '--client-host' expects a single host name as argument - '--client-host' prevede un singolo nome host come argomento - - Specify the port of the client. Specifica la porta del client. @@ -279,11 +351,6 @@ Forza il logger di file predefinito per scrivere il log in modo sincrono. Utile per uno scenario in cui non si vuole perdere alcun log, ad esempio in caso di arresto anomalo del sistema. Si noti che l'esecuzione del test sta rallentando. - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - '--diagnostic-output-fileprefix' prevede un singolo argomento di prefisso del nome file - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag Abilita la registrazione diagnostica. Il livello di log predefinito è 'Trace'. Il file verrà scritto nella directory di output con il nome log_[MMddHHssfff].diag @@ -304,11 +371,6 @@ Directory di output della registrazione diagnostica, se non specificato, il file verrà generato all'interno della directory 'TestResults' predefinita. - - '--diagnostic-output-directory' expects a single directory name argument - '--diagnostic-output-directory' prevede un singolo argomento del nome della directory - - Prefix for the log file name that will replace '[log]_.' Prefisso per il nome del file di log che sostituirà '[log]_.' @@ -324,7 +386,12 @@ Elenca i test disponibili. - + + dotnet test pipe. + pipe di test dotnet. + + + Invalid PID '{0}' {1} PID non valido '{0}' @@ -371,11 +438,6 @@ Non visualizzare il banner di avvio, il messaggio di copyright o il banner di telemetria. - - '--{0}' expects no argument - '--{0}' non prevede alcun argomento - - Specify the port of the server. Specifica la porta del server. @@ -466,6 +528,21 @@ Le istanze di tipo 'ITestFramework' non devono essere registrate tramite il provider di servizi, ma tramite 'ITestApplicationBuilder.RegisterTestFramework' + + Skipped + Ignorato + + + + skipped + ignorato + + + + Stack Trace + Analisi dello stack + + Starting server. Listening on port '{0}' Avvio del server. In ascolto sulla porta '{0}' @@ -561,6 +638,11 @@ Altre informazioni sulla telemetria della piattaforma di test Microsoft: https:/ Non è stato possibile scaricare i log prima del timeout di '{0}' secondi + + Total + Totale + + A filter '{0}' should not contain a '/' character Un filtro '{0}' non dovrebbe contenere un carattere '/' @@ -576,11 +658,6 @@ Altre informazioni sulla telemetria della piattaforma di test Microsoft: https:/ Un carattere di escape non deve terminare la stringa di filtro '{0}' - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - È previsto un singolo argomento, (ad esempio '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - - Only the final filter path can contain '**' wildcard Solo il percorso del filtro finale può contenere il carattere jolly '**' @@ -638,6 +715,11 @@ Altre informazioni sulla telemetria della piattaforma di test Microsoft: https:/ La posizione del programma è ritenuta non raggiungibile. File='{0}', Riga={1} + + Zero tests ran + Nessun test eseguito + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf index 17e99148f9..1f927ca812 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf @@ -2,6 +2,26 @@ + + Aborted + 中止されました + + + + Actual + 実際 + + + + cancelled + キャンセル済み + + + + Cancelling the test session... + テスト セッションを取り消しています... + + Failed to create a test execution filter テスト実行フィルターを作成できませんでした @@ -147,11 +167,28 @@ ディレクトリ '{0}' が見つかりませんでした。 + + Diagnostic file (level '{0}' with async flush): {1}" + 診断ファイル (非同期フラッシュを使用したレベル '{0}'): {1}" + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + 診断ファイル (同期フラッシュを使用したレベル '{0}'): {1}" + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} プロバイダー '{0}' (UID: {1}) が次のエラーで失敗しました: {2} + + Expected + 予想 + + Extension of type '{0}' is not implementing the required '{1}' interface 型 '{0}' の拡張機能は、必要な '{1}' インターフェイスを実装していません @@ -162,6 +199,16 @@ 同じ UID '{0}' を持つ別の拡張機能が既に登録されています。登録されている拡張機能の種類は '{1}' です + + Failed + 失敗 + + + + failed + 失敗 + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + テスト用 + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: 次の 'ITestHostEnvironmentVariableProvider' プロバイダーは、最終的な環境変数のセットアップを拒否しました: @@ -204,6 +256,11 @@ <テスト アプリケーション ランナー> + + In process file artifacts produced: + インプロセスのファイル成果物が生成されました: + + Method '{0}' did not exit successfully メソッド '{0}' が正常に終了しませんでした @@ -239,6 +296,11 @@ テスト プラットフォーム プロトコル仕様に基づく JsonRpc サーバーからクライアント ハンドシェイクへの実装。 + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + 予想されるテストの最小ポリシー違反、テストの実行数 {0} 件、予想される最小数 {1} 件 + {0}, {1} number of tests + No serializer registered with ID '{0}' ID '{0}' で登録されたシリアライザーがありません @@ -259,16 +321,26 @@ 見つかりません + + Out of process file artifacts produced: + アウトプロセスのファイル成果物が生成されました: + + + + Passed + 成功 + + + + passed + 合格しました + + Specify the hostname of the client. クライアントのホスト名を指定します。 - - '--client-host' expects a single host name as argument - '--client-host' には、引数として 1 つのホスト名が必要です - - Specify the port of the client. クライアントのポートを指定します。 @@ -279,11 +351,6 @@ 組み込みのファイル ロガーでログを同期的に書き込むことを強制します。ログを失わないシナリオ (クラッシュした場合など) に役立ちます。これにより、テストの実行速度が低下していることに注意してください。 - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - '--diagnostic-output-fileprefix' には、1 つのファイル名プレフィックス引数が必要です - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag 診断ログを有効にします。既定のログ レベルは 'Trace' です。ファイルは、出力ディレクトリに log_[MMddHHssfff].diag という名前で書き込まれます。 @@ -304,11 +371,6 @@ 診断ログの出力ディレクトリ。指定しない場合ファイルは既定の 'TestResults' ディレクトリ内に生成されます。 - - '--diagnostic-output-directory' expects a single directory name argument - '--diagnostic-output-directory' には単一のディレクトリ名引数が必要です - - Prefix for the log file name that will replace '[log]_.' '[log]_' を置き換えるログ ファイル名のプレフィックス。 @@ -324,7 +386,12 @@ 使用可能なテストを一覧表示します。 - + + dotnet test pipe. + dotnet テスト パイプ。 + + + Invalid PID '{0}' {1} PID '{0}' @@ -372,11 +439,6 @@ スタートアップ バナー、著作権メッセージ、テレメトリ バナーは表示しないでください。 - - '--{0}' expects no argument - '--{0}' には引数が必要ありません - - Specify the port of the server. サーバーのポートを指定します。 @@ -467,6 +529,21 @@ 'ITestFramework' 型のインスタンスは、サービス プロバイダーを介して登録することはできません。'ITestApplicationBuilder.RegisterTestFramework' を介して登録する必要があります + + Skipped + スキップ + + + + skipped + スキップされました + + + + Stack Trace + スタック トレース + + Starting server. Listening on port '{0}' サーバーを起動しています。ポート '{0}' で聞いています @@ -562,6 +639,11 @@ Microsoft Testing Platform テレメトリの詳細: https://aka.ms/testingplatf '{0}' 秒のタイムアウト前にログをフラッシュできませんでした + + Total + 合計 + + A filter '{0}' should not contain a '/' character フィルター '{0}' に '/' 文字を含めることはできません @@ -577,11 +659,6 @@ Microsoft Testing Platform テレメトリの詳細: https://aka.ms/testingplatf エスケープ文字はフィルター文字列 '{0}' を終了できません - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - 1 つの引数が必要です (例: '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - - Only the final filter path can contain '**' wildcard '**' ワイルドカードを含めることができるのは、最後のフィルター パスのみです @@ -639,6 +716,11 @@ Microsoft Testing Platform テレメトリの詳細: https://aka.ms/testingplatf このプログラムの場所に到達できないと考えられます。ファイル='{0}'、行={1} + + Zero tests ran + 0 件のテストが実行されました + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf index 3d26ec9174..46fc9f3128 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf @@ -2,6 +2,26 @@ + + Aborted + 중단됨 + + + + Actual + 실제 + + + + cancelled + 취소됨 + + + + Cancelling the test session... + 테스트 세션을 취소하는 중... + + Failed to create a test execution filter 테스트 실행 필터를 만들지 못함 @@ -147,11 +167,28 @@ {0} 디렉터리를 찾을 수 없음 + + Diagnostic file (level '{0}' with async flush): {1}" + 진단 파일(비동기 플러시를 사용한 수준 '{0}'): {1}" + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + 진단 파일(동기 플러시를 사용한 수준 '{0}'): {1}" + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} 공급자 '{0}'(UID: {1})이 오류 {2}(으)로 실패했습니다. + + Expected + 필요 + + Extension of type '{0}' is not implementing the required '{1}' interface ‘{0}’ 형식의 확장이 필요한 '{1}' 인터페이스를 구현하지 않음 @@ -162,6 +199,16 @@ UID '{0}'과(와) 동일한 다른 확장이 이미 등록되었습니다. 등록된 확장은 '{1}' 형식임 + + Failed + 실패 + + + + failed + 실패 + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + 테스트용 + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: 다음 'ITestHostEnvironmentVariableProvider' 공급자가 최종 환경 변수 설정을 거부했습니다. @@ -204,6 +256,11 @@ <테스트 애플리케이션 실행기> + + In process file artifacts produced: + 생성된 In process 파일 아티팩트: + + Method '{0}' did not exit successfully '{0}' 메서드가 성공적으로 종료되지 않았습니다. @@ -239,6 +296,11 @@ 테스트 플랫폼 프로토콜 사양을 기반으로 구현되는 JsonRpc 서버와 클라이언트의 핸드셰이크입니다. + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + 예상되는 최소 테스트 정책 위반, 테스트 실행 {0}, 필요한 최소 {1} + {0}, {1} number of tests + No serializer registered with ID '{0}' ID '{0}'(으)로 등록된 직렬 변환기가 없습니다. @@ -259,16 +321,26 @@ 찾을 수 없음 + + Out of process file artifacts produced: + 생성된 Out of process 파일 아티팩트: + + + + Passed + 통과 + + + + passed + 통과 + + Specify the hostname of the client. 클라이언트의 호스트 이름을 지정합니다. - - '--client-host' expects a single host name as argument - '--client-host'에는 단일 호스트 이름이 인수로 필요합니다. - - Specify the port of the client. 클라이언트의 포트를 지정합니다. @@ -279,11 +351,6 @@ 기본 제공 파일 로거가 로그를 동기적으로 기록하도록 강제합니다. 로그를 잃지 않으려는 시나리오(예: 충돌 시)에 유용합니다. 이로 인해 테스트 실행 속도가 느려집니다. - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - '--diagnostic-output-fileprefix'에는 단일 파일 이름 접두사 인수가 필요합니다. - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag 진단 로깅을 사용합니다. 기본값 로그 수준은 'Trace'입니다. 파일은 log_[MMddHHssfff].diag라는 이름으로 출력 디렉터리에 기록됩니다. @@ -304,11 +371,6 @@ 진단 로깅의 출력 디렉터리를 지정하지 않으면 기본 'TestResults' 디렉터리 내에 파일이 생성됩니다. - - '--diagnostic-output-directory' expects a single directory name argument - '--diagnostic-output-directory'에는 단일 디렉터리 이름 인수가 필요합니다. - - Prefix for the log file name that will replace '[log]_.' '[log]_'를 대체할 로그 파일 이름의 접두사입니다. @@ -324,7 +386,12 @@ 사용 가능한 테스트를 나열합니다. - + + dotnet test pipe. + .NET 테스트 파이프입니다. + + + Invalid PID '{0}' {1} 잘못된 PID '{0}' @@ -371,11 +438,6 @@ 시작 배너, 저작권 메시지 또는 원격 분석 배너를 표시하지 마세요. - - '--{0}' expects no argument - '--{0}'에는 인수가 필요하지 않습니다. - - Specify the port of the server. 서버의 포트를 지정합니다. @@ -466,6 +528,21 @@ 'ITestFramework' 형식의 인스턴스는 서비스 공급자를 통해 등록하지 않아야 하지만 'ITestApplicationBuilder.RegisterTestFramework'를 통해 등록해야 합니다. + + Skipped + 건너뜀 + + + + skipped + 건너뜀 + + + + Stack Trace + 스택 추적 + + Starting server. Listening on port '{0}' 서버를 시작하는 중입니다. 포트 '{0}'에서 수신 대기 중 @@ -561,6 +638,11 @@ Microsoft 테스트 플랫폼 원격 분석에 대해 자세히 알아보기: ht '{0}'초의 시간 제한 전에 로그를 플러시하지 못했습니다. + + Total + 합계 + + A filter '{0}' should not contain a '/' character '{0}' 필터는 '/' 문자를 포함하면 안 됨 @@ -576,11 +658,6 @@ Microsoft 테스트 플랫폼 원격 분석에 대해 자세히 알아보기: ht 이스케이프 문자가 필터 문자열 '{0}'을(를) 종료하면 안 됨 - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - 단일 인수가 필요합니다(예: '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]'). - - Only the final filter path can contain '**' wildcard 최종 필터 경로만 '**' 와일드카드를 포함할 수 있음 @@ -638,6 +715,11 @@ Microsoft 테스트 플랫폼 원격 분석에 대해 자세히 알아보기: ht 이 프로그램 위치에 연결할 수 없는 것으로 생각됩니다. File='{0}' Line={1} + + Zero tests ran + 테스트가 0개 실행됨 + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf index 52047ab4b5..5098a88d15 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf @@ -2,6 +2,26 @@ + + Aborted + Przerwano + + + + Actual + Rzeczywiste + + + + cancelled + anulowano + + + + Cancelling the test session... + Trwa anulowanie sesji testowej... + + Failed to create a test execution filter Nie można utworzyć filtru wykonywania testu @@ -147,11 +167,28 @@ Nie można odnaleźć katalogu „{0}”. + + Diagnostic file (level '{0}' with async flush): {1}" + Plik diagnostyczny (poziom „{0}” z asynchronicznym opróżnianiem): {1}„ + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + Plik diagnostyczny (poziom „{0}” z synchronicznym opróżnianiem): {1}„ + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} Dostawca „{0}” (UID:{1}) nie powiódł się z powodu błędu: {2} + + Expected + Oczekiwane + + Extension of type '{0}' is not implementing the required '{1}' interface Rozszerzenie typu „{0}” nie implementuje wymaganego interfejsu „{1}”. @@ -162,6 +199,16 @@ Inne rozszerzenie o tym samym identyfikatorze UID „{0}” zostało już zarejestrowane. Zarejestrowane rozszerzenie jest typu „{1}” + + Failed + Niepowodzenie + + + + failed + zakończone niepowodzeniem + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + Na potrzeby testu + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: Następujący dostawcy „ITestHostEnvironmentVariableProvider” odrzucili końcową konfigurację zmiennych środowiskowych: @@ -204,6 +256,11 @@ <test application runner> + + In process file artifacts produced: + Wygenerowane artefakty pliku w trakcie procesu: + + Method '{0}' did not exit successfully Metoda „{0}” nie zakończyła się pomyślnie @@ -239,6 +296,11 @@ Serwer JsonRpc do uzgadniania klienta, implementacja oparta na specyfikacji protokołu platformy testowej. + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + Minimalne oczekiwane naruszenie zasad testów, uruchomione testy: {0}, oczekiwane minimum: {1} + {0}, {1} number of tests + No serializer registered with ID '{0}' Nie zarejestrowano serializatora z identyfikatorem „{0}” @@ -259,16 +321,26 @@ Nie znaleziono + + Out of process file artifacts produced: + Wygenerowane artefakty pliku poza procesem: + + + + Passed + Powodzenie + + + + passed + zakończone powodzeniem + + Specify the hostname of the client. Określ nazwę hosta klienta. - - '--client-host' expects a single host name as argument - Element „--client-host” oczekuje pojedynczej nazwy hosta jako argumentu - - Specify the port of the client. Określ port klienta. @@ -279,11 +351,6 @@ Wymuś synchroniczne zapisywanie dziennika przez wbudowany rejestrator plików. Przydatne w przypadku scenariusza, w którym nie chcesz utracić żadnego dziennika (tj. w przypadku awarii). Pamiętaj, że to spowalnia wykonywanie testu. - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - Element „--diagnostic-output-fileprefix” oczekuje pojedynczego argumentu prefiksu nazwy pliku - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag Włącz rejestrowanie diagnostyczne. Domyślny poziom dziennika to „Śledzenie”. Plik zostanie zapisany w katalogu wyjściowym o nazwie log_[MMddHHssfff].diag @@ -304,11 +371,6 @@ Katalog wyjściowy rejestrowania diagnostycznego, jeśli nie zostanie określony, plik zostanie wygenerowany w domyślnym katalogu „TestResults”. - - '--diagnostic-output-directory' expects a single directory name argument - Opcja „--diagnostic-output-directory” oczekuje pojedynczego argumentu nazwy katalogu - - Prefix for the log file name that will replace '[log]_.' Prefiks nazwy pliku dziennika, który zastąpi element „[log]_”. @@ -324,7 +386,12 @@ Wyświetl listę dostępnych testów. - + + dotnet test pipe. + potok testu dotnet. + + + Invalid PID '{0}' {1} Nieprawidłowy identyfikator PID „{0}” @@ -371,11 +438,6 @@ Nie wyświetlaj baneru startowego, komunikatu o prawach autorskich ani transparentu telemetrii. - - '--{0}' expects no argument - Opcja „--{0}” nie oczekuje żadnego argumentu - - Specify the port of the server. Określ port serwera. @@ -466,6 +528,21 @@ Wystąpienia typu „ITestFramework” nie powinny być rejestrowane za pośrednictwem dostawcy usług, ale za pośrednictwem elementu „ITestApplicationBuilder.RegisterTestFramework” + + Skipped + Pominięto + + + + skipped + pominięte + + + + Stack Trace + Śledzenie stosu + + Starting server. Listening on port '{0}' Uruchamianie serwera. Nasłuchiwanie na porcie „{0}” @@ -561,6 +638,11 @@ Więcej informacji o telemetrii platformy testowej firmy Microsoft: https://aka. Nie można opróżnić dzienników przed upływem limitu czasu wynoszącego „{0}” s + + Total + Łącznie + + A filter '{0}' should not contain a '/' character Filtr „{0}” nie powinien zawierać znaku „/” @@ -576,11 +658,6 @@ Więcej informacji o telemetrii platformy testowej firmy Microsoft: https://aka. Znak ucieczki nie powinien kończyć ciągu filtru „{0}” - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - Oczekiwano pojedynczego argumentu (np. „/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]”) - - Only the final filter path can contain '**' wildcard Tylko końcowa ścieżka filtru może zawierać symbol wieloznaczny „**” @@ -638,6 +715,11 @@ Więcej informacji o telemetrii platformy testowej firmy Microsoft: https://aka. Ta lokalizacja programu jest uważana za nieosiągalną. Plik=„{0}”, Wiersz={1} + + Zero tests ran + Uruchomiono zero testów + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf index e644550f03..f2b5538695 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf @@ -2,6 +2,26 @@ + + Aborted + Anulado + + + + Actual + Real + + + + cancelled + Cancelado + + + + Cancelling the test session... + Cancelando a sessão de teste... + + Failed to create a test execution filter Falha ao criar um filtro de execução de teste @@ -147,11 +167,28 @@ Não foi possível localizar o diretório “{0}” + + Diagnostic file (level '{0}' with async flush): {1}" + Arquivo de diagnóstico (nível "{0}" com liberação assíncrona): {1}" + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + Arquivo de diagnóstico (nível "{0}" com liberação de sincronização): {1}" + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} O provedor ''{0}'' (UID: {1}) falhou com o erro: {2} + + Expected + Esperado + + Extension of type '{0}' is not implementing the required '{1}' interface A extensão do tipo “{0}” não está implementando a interface “{1}” necessária @@ -162,6 +199,16 @@ Outra extensão com o mesmo UID “{0}” já foi registrada. A extensão registrada é do tipo “{1}” + + Failed + Falhou + + + + failed + com falha + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + Para teste + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: Os seguintes provedores ''ITestHostEnvironmentVariableProvider'' rejeitaram a configuração de variáveis de ambiente finais: @@ -204,6 +256,11 @@ <test application runner> + + In process file artifacts produced: + Artefatos de arquivo de processo produzidos: + + Method '{0}' did not exit successfully O método “{0}” não foi encerrado com êxito @@ -239,6 +296,11 @@ Handshake de servidor JsonRpc para cliente, implementação baseada na especificação do protocolo da plataforma de teste. + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + Violação de política de testes mínimos esperados, {0} testes executados, mínimo esperado {1} + {0}, {1} number of tests + No serializer registered with ID '{0}' Nenhum serializador registrado com a ID “{0}” @@ -259,16 +321,26 @@ Não encontrado + + Out of process file artifacts produced: + Artefatos de arquivo fora do processo produzidos: + + + + Passed + Aprovado + + + + passed + aprovado + + Specify the hostname of the client. Especifique o nome do host do cliente. - - '--client-host' expects a single host name as argument - '--client-host' espera um único nome de host como argumento - - Specify the port of the client. Especifique a porta do cliente. @@ -279,11 +351,6 @@ Force o agente de arquivo interno a gravar o log de forma síncrona. Útil para cenários em que você não deseja perder nenhum log (ou seja, em caso de falha). Observe que isso está diminuindo a execução do teste. - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - '--diagnostic-output-fileprefix' espera um único argumento de prefixo de nome de arquivo - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag Habilite o log de diagnóstico. O nível de log padrão é “Trace”. O arquivo será gravado no diretório de saída com o nome log_[MMddHHssfff].diag @@ -304,11 +371,6 @@ Diretório de saída do log de diagnóstico, se não especificado, o arquivo será gerado dentro do diretório padrão 'TestResults'. - - '--diagnostic-output-directory' expects a single directory name argument - '--diagnostic-output-directory' espera um argumento de nome de diretório único - - Prefix for the log file name that will replace '[log]_.' Prefixo do nome do arquivo de log que substituirá '[log]_.' @@ -324,7 +386,12 @@ Listar testes disponíveis. - + + dotnet test pipe. + pipe de teste dotnet. + + + Invalid PID '{0}' {1} PID inválido "{0}" @@ -371,11 +438,6 @@ Não exiba a faixa de inicialização, a mensagem de direitos autorais ou a faixa de telemetria. - - '--{0}' expects no argument - '--{0}' não espera nenhum argumento - - Specify the port of the server. Especifique a porta do servidor. @@ -466,6 +528,21 @@ Instâncias do tipo "ITestFramework" não devem ser registradas por meio do provedor de serviços, mas por meio de "ITestApplicationBuilder.RegisterTestFramework" + + Skipped + Ignorado + + + + skipped + ignorado + + + + Stack Trace + Rastreamento de Pilha + + Starting server. Listening on port '{0}' Iniciando servidor. Escutando na porta “{0}” @@ -561,6 +638,11 @@ Leia mais sobre a telemetria da Plataforma de Testes da Microsoft: https://aka.m Falha ao liberar logs antes do tempo limite de '{0}' segundos + + Total + Total + + A filter '{0}' should not contain a '/' character Um filtro “{0}” não deve conter um caractere '/' @@ -576,11 +658,6 @@ Leia mais sobre a telemetria da Plataforma de Testes da Microsoft: https://aka.m Um caractere de escape não deve encerrar a cadeia de caracteres de filtro “{0}” - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - Um único argumento é esperado (por exemplo, '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - - Only the final filter path can contain '**' wildcard Somente o caminho do filtro final pode conter curinga “**” @@ -638,6 +715,11 @@ Leia mais sobre a telemetria da Plataforma de Testes da Microsoft: https://aka.m Este local do programa é considerado inacessível. Arquivo='{0}' Linha={1} + + Zero tests ran + Zero testes executados + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf index 5e9bb3da2d..5489a38387 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf @@ -2,6 +2,26 @@ + + Aborted + Прервано + + + + Actual + Фактические + + + + cancelled + отменено + + + + Cancelling the test session... + Отмена тестового сеанса... + + Failed to create a test execution filter Не удалось создать фильтр выполнения теста @@ -147,11 +167,28 @@ Не удалось найти каталог "{0}". + + Diagnostic file (level '{0}' with async flush): {1}" + Диагностический файл (уровень "{0}" с асинхронной очисткой): "{1}" + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + Диагностический файл (уровень "{0}" с очисткой синхронизации): "{1}" + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} Сбой поставщика "{0}" (ИД пользователя: {1}) с ошибкой: {2} + + Expected + Ожидалось + + Extension of type '{0}' is not implementing the required '{1}' interface Расширение типа "{0}" не реализует требуемый интерфейс "{1}" @@ -162,6 +199,16 @@ Уже зарегистрировано другое расширение с таким же ИД пользователя "{0}". Зарегистрированное расширение относится к типу "{1}" + + Failed + Сбой + + + + failed + сбой + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + Для тестирования + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: Следующие поставщики "ITestHostEnvironmentVariableProvider" отклонили настройку окончательных переменных среды: @@ -204,6 +256,11 @@ <проверка средства выполнения тестов приложения> + + In process file artifacts produced: + Созданные в процессе артефакты файлов: + + Method '{0}' did not exit successfully Сбой выхода метода "{0}" @@ -239,6 +296,11 @@ Рукопожатие сервера JsonRpc с клиентом, реализация на основе спецификации протокола платформы тестирования. + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + Нарушение политики минимального ожидаемого числа тестов, тесты выполнено: {0}, ожидаемый минимум: {1} + {0}, {1} number of tests + No serializer registered with ID '{0}' Не зарегистрирован сериализатор с ИД "{0}" @@ -259,16 +321,26 @@ Не найдено + + Out of process file artifacts produced: + Созданные вне процесса артефакты файлов: + + + + Passed + Пройден + + + + passed + пройдено + + Specify the hostname of the client. Укажите имя узла клиента. - - '--client-host' expects a single host name as argument - "--client-host" ожидает в качестве аргумента одно имя узла - - Specify the port of the client. Укажите порт клиента. @@ -279,11 +351,6 @@ Принудительная синхронная запись журнала с помощью встроенного средства ведения журнала файла. Удобно для сценария, в котором вы не хотите потерять журнал (например, в случае сбоя). Обратите внимание, что это замедляет выполнение теста. - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - "--diagnostic-output-fileprefix" ожидает один аргумент в виде префикса имени файла - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag Включить ведение журнала диагностики. Уровень журнала по умолчанию — "Трассировка". Файл с именем log_[MMddHHssfff].diag будет записан в выходной каталог @@ -304,11 +371,6 @@ Выходной каталог журнала диагностики, если не указан, файл будет создан в каталоге "TestResults" по умолчанию. - - '--diagnostic-output-directory' expects a single directory name argument - "--diagnostic-output-directory" ожидает один аргумент в виде имени каталога - - Prefix for the log file name that will replace '[log]_.' Префикс для имени файла журнала, который заменит "[log]_." @@ -324,7 +386,12 @@ Список доступных тестов. - + + dotnet test pipe. + тестовый канал dotnet. + + + Invalid PID '{0}' {1} Недопустимый PID "{0}" @@ -371,11 +438,6 @@ Не отображать баннер запуска, сообщение об авторских правах и баннер телеметрии. - - '--{0}' expects no argument - "--{0}" не ожидает никаких аргументов - - Specify the port of the server. Укажите порт сервера. @@ -466,6 +528,21 @@ Экземпляры типа "ITestFramework" следует регистрировать не через поставщика услуг, а через "ITestApplicationBuilder.RegisterTestFramework" + + Skipped + Пропущен + + + + skipped + пропущено + + + + Stack Trace + Трассировка стека + + Starting server. Listening on port '{0}' Выполняется запуск сервера. Прослушивание порта "{0}" @@ -561,6 +638,11 @@ Read more about Microsoft Testing Platform telemetry: https://aka.ms/testingplat Не удалось записать журналы на диск до истечения времени ожидания ("{0}" с) + + Total + Всего + + A filter '{0}' should not contain a '/' character Фильтр "{0}" не должен содержать символ "/" @@ -576,11 +658,6 @@ Read more about Microsoft Testing Platform telemetry: https://aka.ms/testingplat Escape-символ не должен завершать строку фильтра "{0}" - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - Ожидается один аргумент (например, "/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]") - - Only the final filter path can contain '**' wildcard Только конечный путь фильтра может содержать подстановочный знак "**" @@ -638,6 +715,11 @@ Read more about Microsoft Testing Platform telemetry: https://aka.ms/testingplat Похоже, что это расположение программы является недоступным. Файл="{0}", строка={1} + + Zero tests ran + Запущено ноль тестов + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf index eec69238ed..88d96d0fad 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf @@ -2,6 +2,26 @@ + + Aborted + Durduruldu + + + + Actual + Fiili + + + + cancelled + iptal edildi + + + + Cancelling the test session... + Test oturumu iptal ediliyor... + + Failed to create a test execution filter Test yürütme filtresi oluşturulamadı @@ -147,11 +167,28 @@ '{0}' dizini bulunamadı + + Diagnostic file (level '{0}' with async flush): {1}" + Tanılama dosyası (zaman uyumsuz temizleme ile düzey '{0}'): {1}" + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + Tanılama dosyası (zaman uyumlu temizleme ile düzey '{0}' ): {1}" + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} '{0}' sağlayıcısı '' (UID: {1}) şu hatayla başarısız oldu: {2} + + Expected + Beklenen + + Extension of type '{0}' is not implementing the required '{1}' interface '{0}' uzantı türü, gerekli '{1}' arabirimini uygulamıyor @@ -162,6 +199,16 @@ Aynı UID '{0}' olan başka bir uzantı zaten kayıtlı. Kayıtlı uzantı '{1}' türünde + + Failed + Başarısız + + + + failed + başarısız + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + Test için + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: Aşağıdaki 'ITestHostEnvironmentVariableProvider' sağlayıcıları son ortam değişkenleri kurulumunu reddetti: @@ -204,6 +256,11 @@ <test uygulama çalıştırıcısı> + + In process file artifacts produced: + Üretilen işlem içi dosya yapıtları: + + Method '{0}' did not exit successfully '{0}' yöntemi başarıyla çıkmadı @@ -239,6 +296,11 @@ JsonRpc sunucusundan istemciye el sıkışması, test platformu protokol belirtimine dayalı uygulama. + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + Beklenen minimum test ilke ihlali, yürütülen test: {0}, beklenen minimum test: {1} + {0}, {1} number of tests + No serializer registered with ID '{0}' '{0}' kimliğiyle kayıtlı seri hale getirici yok @@ -259,16 +321,26 @@ Bulunamadı + + Out of process file artifacts produced: + Üretilen işlem dışı dosya yapıtları: + + + + Passed + Başarılı + + + + passed + başarılı + + Specify the hostname of the client. İstemcinin ana bilgisayar adını belirtir. - - '--client-host' expects a single host name as argument - '--client-host' bağımsız değişken olarak tek bir ana bilgisayar adı bekler - - Specify the port of the client. İstemcinin bağlantı noktasını belirtir. @@ -279,11 +351,6 @@ Yerleşik dosya kaydediciyi günlüğü eşzamanlı olarak yazmaya zorlar. Herhangi bir günlüğü (kilitlenme durumunda) kaybetmek istemediğiniz senaryolar için faydalıdır. Bunun test yürütmesini yavaşlatacağını unutmayın. - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - '--diagnostic-output-fileprefix' tek bir dosya adı ön ek bağımsız değişkeni bekler - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag Tanılama günlük kaydını etkinleştirir. Varsayılan günlük düzeyi: 'İzleme'. Dosya, çıkış dizinine log_[MMddHHssfff].diag adıyla yazılır @@ -304,11 +371,6 @@ Tanılama günlüğüne kaydetme için çıkış dizini belirtilmezse dosya varsayılan 'TestResults' dizini içinde oluşturulur. - - '--diagnostic-output-directory' expects a single directory name argument - '--diagnostic-output-directory' tek bir dizin adı bağımsız değişkeni bekliyor - - Prefix for the log file name that will replace '[log]_.' '[log]_' öğesinin yerini alan günlük dosyası adına önek uygular. @@ -324,7 +386,12 @@ Kullanılabilir testleri listeler. - + + dotnet test pipe. + dotnet test kanalı. + + + Invalid PID '{0}' {1} Geçersiz PID '{0}' @@ -371,11 +438,6 @@ Başlangıç başlığını, telif hakkı iletisini veya telemetri başlığını görüntülemez. - - '--{0}' expects no argument - '--{0}' bağımsız değişken beklemiyor - - Specify the port of the server. Sunucunun bağlantı noktasını belirtir. @@ -466,6 +528,21 @@ 'ITestFramework' türündeki örnekler hizmet sağlayıcısı aracılığıyla değil, 'ITestApplicationBuilder.RegisterTestFramework' aracılığıyla kaydedilmelidir + + Skipped + Atlandı + + + + skipped + atlandı + + + + Stack Trace + Yığın İzlemesi + + Starting server. Listening on port '{0}' Sunucu başlatılıyor. '{0}' bağlantı noktasında dinleme işlemi yapılıyor @@ -561,6 +638,11 @@ Microsoft Test Platformu telemetrisi hakkında daha fazla bilgi edinin: https:// '{0}' saniyelik zaman aşımından önce günlükler boşaltılamadı + + Total + Toplam + + A filter '{0}' should not contain a '/' character '{0}' filtresi, '/' karakteri içermemelidir @@ -576,11 +658,6 @@ Microsoft Test Platformu telemetrisi hakkında daha fazla bilgi edinin: https:// Kaçış karakteri, '{0}' filtre dizesini sonlandırmamalıdır - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - Tek bir bağımsız değişken bekleniyor (ör. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - - Only the final filter path can contain '**' wildcard Yalnızca son filtre yolu, '**' joker karakterlerini içerebilir @@ -638,6 +715,11 @@ Microsoft Test Platformu telemetrisi hakkında daha fazla bilgi edinin: https:// Bu program konumu erişilemez olarak görülüyor. Dosya='{0}', Satır={1} + + Zero tests ran + Sıfır test çalıştırıldı + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf index 179b601a57..b53bfe27af 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf @@ -2,6 +2,26 @@ + + Aborted + 已中止 + + + + Actual + 实际 + + + + cancelled + 已取消 + + + + Cancelling the test session... + 正在取消测试会话... + + Failed to create a test execution filter 未能创建测试执行筛选 @@ -147,11 +167,28 @@ 找不到目录“{0}” + + Diagnostic file (level '{0}' with async flush): {1}" + 诊断文件(具有异步刷新的级别“{0}”): {1}” + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + 诊断文件(具有同步刷新的级别“{0}”): {1}” + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} 提供程序 '{0}' (UID: {1}) 失败,出现错误: {2} + + Expected + 预期 + + Extension of type '{0}' is not implementing the required '{1}' interface 类型“{0}”的扩展未实现所需的“{1}”接口 @@ -162,6 +199,16 @@ 已注册另一个具有相同 UID“{0}”的扩展。已注册的扩展属于“{1}”类型 + + Failed + 失败 + + + + failed + 失败 + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + 用于测试 + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: 以下 'ITestHostEnvironmentVariableProvider' 提供程序拒绝了最终的环境变量设置: @@ -204,6 +256,11 @@ <test application runner> + + In process file artifacts produced: + 生成的进程内文件项目: + + Method '{0}' did not exit successfully 方法“{0}”未成功退出 @@ -239,6 +296,11 @@ 基于测试平台协议规范的 JsonRpc 服务器到客户端握手实现。 + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + 最低预期测试策略冲突,测试已运行 {0} 次,最低预期 {1} 次 + {0}, {1} number of tests + No serializer registered with ID '{0}' 没有使用 ID“{0}”注册序列化程序 @@ -259,16 +321,26 @@ 未找到 + + Out of process file artifacts produced: + 生成的进程外文件项目: + + + + Passed + 已通过 + + + + passed + 已通过 + + Specify the hostname of the client. 指定客户端的主机名。 - - '--client-host' expects a single host name as argument - “--client-host”应有单一主机名作为参数 - - Specify the port of the client. 指定客户端的端口。 @@ -279,11 +351,6 @@ 强制内置文件记录器同步写入日志。适用于不想丢失任何日志的情况 (即在故障的情况下)。请注意,这会减慢测试的执行速度。 - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - “--diagnostic-output-fileprefix”应有单一文件名前缀参数 - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag 启用诊断日志记录。默认日志级别为 "Trace"。文件将写入输出目录,名称为 log_[MMddHHssfff].diag @@ -304,11 +371,6 @@ 诊断日志记录的输出目录,如果未指定,则文件将在默认的“TestResults”目录中生成。 - - '--diagnostic-output-directory' expects a single directory name argument - “--diagnostic-output-directory”应有单一目录名称参数 - - Prefix for the log file name that will replace '[log]_.' 将替换“[log]_”的日志文件名的前缀 @@ -324,7 +386,12 @@ 列出可用的测试。 - + + dotnet test pipe. + dotnet 测试管道。 + + + Invalid PID '{0}' {1} 无效的 PID“{0}” @@ -371,11 +438,6 @@ 请勿显示启动横幅、版权信息或遥测横幅。 - - '--{0}' expects no argument - “--{0}”不应有参数 - - Specify the port of the server. 指定服务器的端口。 @@ -466,6 +528,21 @@ 不应通过服务提供商注册类型为“ITestFramework”的实例,而应通过“ITestApplicationBuilder.RegisterTestFramework”进行注册 + + Skipped + 已跳过 + + + + skipped + 已跳过 + + + + Stack Trace + 堆栈跟踪 + + Starting server. Listening on port '{0}' 正在启动服务器。正在侦听端口“{0}” @@ -561,6 +638,11 @@ Microsoft 测试平台会收集使用数据,帮助我们改善体验。该数 未能在“{0}”秒超时之前刷新日志 + + Total + 总计 + + A filter '{0}' should not contain a '/' character 筛选“{0}”不应包含“/”字符 @@ -576,11 +658,6 @@ Microsoft 测试平台会收集使用数据,帮助我们改善体验。该数 转义字符不应终止筛选字符串“{0}” - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - 需要单一参数 (例如,'/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - - Only the final filter path can contain '**' wildcard 只有最终筛选路径可以包含“**”通配符 @@ -638,6 +715,11 @@ Microsoft 测试平台会收集使用数据,帮助我们改善体验。该数 此程序位置被视为无法访问。文件='{0}',行={1} + + Zero tests ran + 运行了零个测试 + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf index df46ce47fe..54bf568da3 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf @@ -2,6 +2,26 @@ + + Aborted + 已中止 + + + + Actual + 實際 + + + + cancelled + 已取消 + + + + Cancelling the test session... + 正在取消測試工作階段... + + Failed to create a test execution filter 無法建立測試執行篩選條件 @@ -147,11 +167,28 @@ 找不到目錄 '{0}' + + Diagnostic file (level '{0}' with async flush): {1}" + 診斷檔案 (具有非同步排清的層級 '{0}'):{1}” + 0 level such as verbose, +1 path to file + + + Diagnostic file (level '{0}' with sync flush): {1}" + 診斷檔案 (具有同步排清的層級 '{0}'):{1}” + 0 level such as verbose, +1 path to file + Provider '{0}' (UID: {1}) failed with error: {2} 提供者 '{0}' (UID: {1}) 失敗,發生錯誤: {2} + + Expected + 預期 + + Extension of type '{0}' is not implementing the required '{1}' interface 類型 '{0}' 的延伸模組未實作所需的 '{1}' 介面 @@ -162,6 +199,16 @@ 已註冊另一個具有相同 UID '{0}' 的延伸模組。已註冊的延伸模組屬於 '{1}' 類型 + + Failed + 失敗 + + + + failed + 已失敗 + + Failed to write the log to the channel. Missed log content: {0} @@ -169,6 +216,11 @@ {0} + + For test + 用於測試 + is followed by test name + The following 'ITestHostEnvironmentVariableProvider' providers rejected the final environment variables setup: 以下 'ITestHostEnvironmentVariableProvider' 提供者拒絕了最終環境變數設定: @@ -204,6 +256,11 @@ <測試應用程式執行器> + + In process file artifacts produced: + 產生的流程内檔案成品: + + Method '{0}' did not exit successfully 方法 '{0}' 未成功結束 @@ -239,6 +296,11 @@ JsonRpc 伺服器至用戶端交握,根據測試平台通訊協定規格的 JsonRpc 伺服器實作。 + + Minimum expected tests policy violation, tests ran {0}, minimum expected {1} + 預期測試原則違反的最小值,{0} 個執行的測試,預期的最小值為 {1} + {0}, {1} number of tests + No serializer registered with ID '{0}' 沒有使用識別碼 '{0}' 註冊的序列化程式 @@ -259,16 +321,26 @@ 找不到 + + Out of process file artifacts produced: + 產生的流程外檔案成品: + + + + Passed + 成功 + + + + passed + 已通過 + + Specify the hostname of the client. 指定用戶端的主機名稱。 - - '--client-host' expects a single host name as argument - '--client-host' 需要單一主機名稱做為引數 - - Specify the port of the client. 指定用戶端的連接埠。 @@ -279,11 +351,6 @@ 強制內建檔案記錄器以同步方式寫入記錄。適用於不想遺失任何記錄的案例 (例如損毀)。請注意,這會減慢測試執行的速度。 - - '--diagnostic-output-fileprefix' expects a single file name prefix argument - '--diagnostic-output-fileprefix' 需要單一檔案名稱前置詞引數 - - Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag 啟用診斷記錄。預設記錄層級為 'Trace'。檔案將以 log_[MMddHHssfff].diag 名稱寫入輸出目錄中 @@ -304,11 +371,6 @@ 診斷記錄的輸出目錄,若未指定,則會在預設的 'TestResults' 目錄內產生檔案。 - - '--diagnostic-output-directory' expects a single directory name argument - '--diagnostic-output-directory' 需要單一目錄名稱引數 - - Prefix for the log file name that will replace '[log]_.' 將取代 '[log]_' 的記錄檔名稱前置詞 @@ -324,7 +386,12 @@ 列出可用的測試。 - + + dotnet test pipe. + dotnet 測試管道。 + + + Invalid PID '{0}' {1} 無效的 PID '{0}' @@ -371,11 +438,6 @@ 不要顯示啟動橫幅、著作權訊息或遙測橫幅。 - - '--{0}' expects no argument - '--{0}' 不需要引數 - - Specify the port of the server. 指定伺服器的連接埠。 @@ -466,6 +528,21 @@ 類型 'ITestFramework' 的執行個體不應透過服務提供者註冊,而是透過 'ITestApplicationBuilder.RegisterTestFramework' 註冊 + + Skipped + 略過 + + + + skipped + 已跳過 + + + + Stack Trace + 堆疊追蹤 + + Starting server. Listening on port '{0}' 正在啟動伺服器。正在連接埠 '{0}' 上聆聽 @@ -561,6 +638,11 @@ Microsoft 測試平台會收集使用量資料,以協助我們改善您的體 無法在 '{0}' 秒逾時前排清記錄 + + Total + 總計 + + A filter '{0}' should not contain a '/' character 篩選條件 '{0}' 不應包含 '/' 字元 @@ -576,11 +658,6 @@ Microsoft 測試平台會收集使用量資料,以協助我們改善您的體 逸出字元不應終止篩選條件字串 '{0}' - - A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - 需要單一引數 (例如 '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]') - - Only the final filter path can contain '**' wildcard 只有最終篩選條件路徑可以包含 '**' 萬用字元 @@ -638,6 +715,11 @@ Microsoft 測試平台會收集使用量資料,以協助我們改善您的體 此程式位置被認為無法連線。File='{0}' Line={1} + + Zero tests ran + 已執行零項測試 + + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/IServerModeManager.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/IServerModeManager.cs index 5116cf113a..dde41a1521 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/IServerModeManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/IServerModeManager.cs @@ -3,6 +3,4 @@ namespace Microsoft.Testing.Platform.ServerMode; -internal interface IServerModeManager -{ -} +internal interface IServerModeManager; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/Json.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/Json.cs index c7b7985732..a0a4be17b8 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/Json.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/Json.cs @@ -80,8 +80,9 @@ public Json(Dictionary? serializers = null, Dictionary(response => { - var list = new (string, object?)[2] + var list = new (string, object?)[3] { + (JsonRpcStrings.ProcessId, response.ProcessId), (JsonRpcStrings.ServerInfo, response.ServerInfo), (JsonRpcStrings.Capabilities, response.Capabilities), }; @@ -136,7 +137,7 @@ public Json(Dictionary? serializers = null, Dictionary(response => Array.Empty<(string, object?)>()); + _serializers[typeof(DiscoverResponseArgs)] = new JsonObjectSerializer(response => []); _serializers[typeof(RunResponseArgs)] = new JsonObjectSerializer(response => { @@ -172,11 +173,11 @@ public Json(Dictionary? serializers = null, Dictionary(message => { - List<(string Name, object? Value)> properties = new() - { - (JsonRpcStrings.Uid, message.Uid.Value), - (JsonRpcStrings.DisplayName, message.DisplayName), - }; + List<(string Name, object? Value)> properties = + [ + (JsonRpcStrings.Uid, message.Uid.Value), + (JsonRpcStrings.DisplayName, message.DisplayName) + ]; TestMetadataProperty[] metadataProperties = message.Properties.OfType(); bool hasActionNodeType = false; @@ -257,10 +258,10 @@ public Json(Dictionary? serializers = null, Dictionary? serializers = null, Dictionary? serializers = null, Dictionary? serializers = null, Dictionary? serializers = null, Dictionary( (json, jsonElement) => new InitializeResponseArgs( + ProcessId: json.Bind(jsonElement, JsonRpcStrings.ProcessId), ServerInfo: json.Bind(jsonElement, JsonRpcStrings.ServerInfo), Capabilities: json.Bind(jsonElement, JsonRpcStrings.Capabilities))); @@ -710,7 +715,7 @@ internal bool TryArrayBind(JsonElement element, out T[]? value, string? prope return false; } - value = element.EnumerateArray().Select(x => Deserialize(x)).ToArray(); + value = element.EnumerateArray().Select(Deserialize).ToArray(); return true; } diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/JsonDeserializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/JsonDeserializer.cs index 61d3a03d5f..aaf5d5f1b4 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/JsonDeserializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/JsonDeserializer.cs @@ -3,6 +3,4 @@ namespace Microsoft.Testing.Platform.ServerMode.Json; -internal abstract class JsonDeserializer -{ -} +internal abstract class JsonDeserializer; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/JsonSerializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/JsonSerializer.cs index 0058479e99..cfd1a0fd2a 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/JsonSerializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/JsonSerializer.cs @@ -3,6 +3,4 @@ namespace Microsoft.Testing.Platform.ServerMode.Json; -internal abstract class JsonSerializer -{ -} +internal abstract class JsonSerializer; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/Jsonite/Jsonite.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/Jsonite/Jsonite.cs index 61ff762b28..42e2ed1c1f 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/Jsonite/Jsonite.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/Jsonite/Jsonite.cs @@ -7,17 +7,17 @@ // Copyright(c) 2016, Alexandre Mutel // All rights reserved. -// +// // Redistribution and use in source and binary forms, with or without modification // , are permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -44,7 +44,7 @@ // [assembly: InternalsVisibleTo("Jsonite.Tests")] // ---------------------------------------------------------------------------- -// This is a single file version of a fast, simple and accurate JSON serializer +// This is a single file version of a fast, simple and accurate JSON serializer // and deserializer. // The serializer should be compatible with the ECMA-404 http://json.org // And the RFC-4627: https://tools.ietf.org/html/rfc4627 @@ -57,7 +57,7 @@ // Version history // ---------------------------------------------------------------------------- // Version 1.0 xoofx, 2016-02-07 -// - Initial version, serializer and deserializer to a simple object +// - Initial version, serializer and deserializer to a simple object // graph. Method for validating a json text. // ---------------------------------------------------------------------------- namespace Jsonite @@ -524,13 +524,13 @@ private object ParseNumber() } while (IsDigit(c)); } - if (c == 'e' || c == 'E') + if (c is 'e' or 'E') { hasExponent = true; builder.Append(c); NextChar(); - if (c == '+' || c == '-') + if (c is '+' or '-') { builder.Append(c); NextChar(); @@ -643,7 +643,7 @@ private object ParseTrue() if (c == 'e') { NextCharSkipWhitespaces(); - return settings.ParseValuesAsStrings ? (object)"true" : true; + return settings.ParseValuesAsStrings ? "true" : true; } } } @@ -666,7 +666,7 @@ private object ParseFalse() if (c == 'e') { NextCharSkipWhitespaces(); - return settings.ParseValuesAsStrings ? (object)"false" : false; + return settings.ParseValuesAsStrings ? "false" : false; } } } @@ -744,25 +744,21 @@ private void NextChar() } [MethodImpl((MethodImplOptions)256)] - private static bool IsWhiteSpace(char c) - { - return c == ' ' || c == '\n' || c == '\t' || c == '\r'; - } + private static bool IsWhiteSpace(char c) => + c is ' ' or '\n' or '\t' or '\r'; [MethodImpl((MethodImplOptions)256)] - private static bool IsDigit(char c) - { - return c >= '0' && c <= '9'; - } + private static bool IsDigit(char c) => + c is >= '0' and <= '9'; [MethodImpl((MethodImplOptions)256)] private static int HexToInt(char c) { - if (c >= '0' && c <= '9') + if (c is >= '0' and <= '9') { return c - '0'; } - if (c >= 'a' && c <= 'f') + if (c is >= 'a' and <= 'f') { return c - 'a' + 10; } @@ -770,12 +766,8 @@ private static int HexToInt(char c) } [MethodImpl((MethodImplOptions)256)] - private static bool IsHex(char c) - { - return (c >= '0' && c <= '9') || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'); - } + private static bool IsHex(char c) => + c is >= '0' and <= '9' or >= 'a' and <= 'f' or >= 'A' and <= 'F'; } /// @@ -1003,16 +995,16 @@ private void WriteString(string text) [MethodImpl((MethodImplOptions)256)] private static bool IsHighSurrogate(char c) { - if ((int)c >= 55296) - return (int)c <= 56319; + if (c >= 55296) + return c <= 56319; return false; } [MethodImpl((MethodImplOptions)256)] private static bool IsLowSurrogate(char c) { - if ((int)c >= 56320) - return (int)c <= 57343; + if (c >= 56320) + return c <= 57343; return false; } @@ -1359,7 +1351,7 @@ interface IJsonReflector void OnDeserializeRaiseParsingError(int offset, int line, int column, string message, Exception inner); /// - /// Called when serializing an object, to determine whether the object is an array or a simple object (with members/properties). + /// Called when serializing an object, to determine whether the object is an array or a simple object (with members/properties). /// This method is then used to correctly route to or . /// /// The object instance being serialized diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/JsoniteProperties.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/JsoniteProperties.cs index c21026c1c5..adf00d8a21 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/JsoniteProperties.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/Json/JsoniteProperties.cs @@ -4,6 +4,4 @@ namespace Microsoft.Testing.Platform.ServerMode.Json; // This object is needed to reuse jsonite's serialization shared code. -internal class JsoniteProperties : Dictionary -{ -} +internal class JsoniteProperties : Dictionary; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/PerRequestServerDataConsumerService.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/PerRequestServerDataConsumerService.cs index a30d8258f3..8da38b8642 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/PerRequestServerDataConsumerService.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/PerRequestServerDataConsumerService.cs @@ -143,7 +143,7 @@ private async Task SendTestNodeUpdatesOnIdleAsync(Guid runId) try { // We subscribe to the per request application lifetime - using CancellationTokenRegistration registration = cancellationToken.Register(() => _testSessionEnd.SetCanceled()); + using CancellationTokenRegistration registration = cancellationToken.Register(_testSessionEnd.SetCanceled); // When batch timer expire or we're at the end of the session we can unblock the message drain ArgumentGuard.IsNotNull(_task); @@ -239,8 +239,7 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella internal void PopulateTestNodeStatistics(TestNodeUpdateMessage message) { - if (message.TestNode is null - || message.TestNode.Properties.SingleOrDefault() is not { } state) + if (message.TestNode.Properties.SingleOrDefault() is not { } state) { return; } diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/RemoteInvocationException.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/RemoteInvocationException.cs index 6027154824..2037f5f31a 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/RemoteInvocationException.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/RemoteInvocationException.cs @@ -3,6 +3,4 @@ namespace Microsoft.Testing.Platform.ServerMode; -internal sealed class RemoteInvocationException : Exception -{ -} +internal sealed class RemoteInvocationException : Exception; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/RpcMessages.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/RpcMessages.cs index e2cd396aa0..621c3d2257 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/RpcMessages.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/RpcMessages.cs @@ -6,9 +6,7 @@ namespace Microsoft.Testing.Platform.ServerMode; -internal abstract record RpcMessage() -{ -} +internal abstract record RpcMessage(); /// /// A request is a message for which the server should return a corresponding @@ -39,7 +37,7 @@ internal record ResponseMessage(int Id, object? Result) : RpcMessage; internal record InitializeRequestArgs(int ProcessId, ClientInfo ClientInfo, ClientCapabilities Capabilities); -internal record InitializeResponseArgs(ServerInfo ServerInfo, ServerCapabilities Capabilities); +internal record InitializeResponseArgs(int? ProcessId, ServerInfo ServerInfo, ServerCapabilities Capabilities); internal record RequestArgsBase(Guid RunId, ICollection? TestNodes, string? GraphFilter); @@ -48,7 +46,7 @@ internal record DiscoverRequestArgs(Guid RunId, ICollection? TestNodes internal record ResponseArgsBase; -internal record DiscoverResponseArgs() : ResponseArgsBase; +internal record DiscoverResponseArgs : ResponseArgsBase; internal record RunRequestArgs(Guid RunId, ICollection? TestNodes, string? GraphFilter) : RequestArgsBase(RunId, TestNodes, GraphFilter); @@ -59,7 +57,7 @@ internal record Artifact(string Uri, string Producer, string Type, string Displa internal record CancelRequestArgs(int CancelRequestId); -internal record ExitRequestArgs(); +internal record ExitRequestArgs; internal record ClientInfo(string Name, string Version); diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/SerializerUtilities.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/SerializerUtilities.cs index 2fbb025827..438e4a058b 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/SerializerUtilities.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/SerializerUtilities.cs @@ -96,6 +96,7 @@ static SerializerUtilities() { Dictionary values = new() { + [JsonRpcStrings.ProcessId] = res.ProcessId, [JsonRpcStrings.ServerInfo] = Serialize(res.ServerInfo), [JsonRpcStrings.Capabilities] = Serialize(res.Capabilities), }; @@ -160,7 +161,7 @@ static SerializerUtilities() Dictionary values = new() { [JsonRpcStrings.RunId] = ev.RunId, - [JsonRpcStrings.Changes] = ev.Changes?.Select(ch => Serialize(ch))?.ToList(), + [JsonRpcStrings.Changes] = ev.Changes?.Select(ch => Serialize(ch)).ToList(), }; return values; @@ -280,8 +281,8 @@ static SerializerUtilities() case FailedTestNodeStateProperty failedTestNodeStateProperty: { properties["execution-state"] = "failed"; - properties["error.message"] = failedTestNodeStateProperty?.Explanation ?? failedTestNodeStateProperty?.Exception?.Message; - if (failedTestNodeStateProperty?.Exception != null) + properties["error.message"] = failedTestNodeStateProperty.Explanation ?? failedTestNodeStateProperty.Exception?.Message; + if (failedTestNodeStateProperty.Exception != null) { Exception exception = failedTestNodeStateProperty.Exception; properties["error.stacktrace"] = exception.StackTrace ?? string.Empty; @@ -295,10 +296,10 @@ static SerializerUtilities() case TimeoutTestNodeStateProperty timeoutTestNodeStateProperty: { properties["execution-state"] = "timed-out"; - properties["error.message"] = timeoutTestNodeStateProperty?.Explanation ?? timeoutTestNodeStateProperty?.Exception?.Message; - if (timeoutTestNodeStateProperty?.Exception != null) + properties["error.message"] = timeoutTestNodeStateProperty.Explanation ?? timeoutTestNodeStateProperty.Exception?.Message; + if (timeoutTestNodeStateProperty.Exception != null) { - properties["error.stacktrace"] = timeoutTestNodeStateProperty?.Exception?.StackTrace ?? string.Empty; + properties["error.stacktrace"] = timeoutTestNodeStateProperty.Exception.StackTrace ?? string.Empty; } break; @@ -307,10 +308,10 @@ static SerializerUtilities() case ErrorTestNodeStateProperty errorTestNodeStateProperty: { properties["execution-state"] = "error"; - properties["error.message"] = errorTestNodeStateProperty?.Explanation ?? errorTestNodeStateProperty?.Exception?.Message; - if (errorTestNodeStateProperty?.Exception != null) + properties["error.message"] = errorTestNodeStateProperty.Explanation ?? errorTestNodeStateProperty.Exception?.Message; + if (errorTestNodeStateProperty.Exception != null) { - properties["error.stacktrace"] = errorTestNodeStateProperty?.Exception?.StackTrace ?? string.Empty; + properties["error.stacktrace"] = errorTestNodeStateProperty.Exception.StackTrace ?? string.Empty; } break; @@ -319,10 +320,10 @@ static SerializerUtilities() case CancelledTestNodeStateProperty cancelledTestNodeStateProperty: { properties["execution-state"] = "cancelled"; - properties["error.message"] = cancelledTestNodeStateProperty?.Explanation ?? cancelledTestNodeStateProperty?.Exception?.Message; - if (cancelledTestNodeStateProperty?.Exception != null) + properties["error.message"] = cancelledTestNodeStateProperty.Explanation ?? cancelledTestNodeStateProperty.Exception?.Message; + if (cancelledTestNodeStateProperty.Exception != null) { - properties["error.stacktrace"] = cancelledTestNodeStateProperty?.Exception?.StackTrace ?? string.Empty; + properties["error.stacktrace"] = cancelledTestNodeStateProperty.Exception.StackTrace ?? string.Empty; } break; @@ -515,10 +516,11 @@ static SerializerUtilities() Deserializers[typeof(InitializeResponseArgs)] = new ObjectDeserializer(properties => { + int processId = GetRequiredPropertyFromJson(properties, JsonRpcStrings.ProcessId); ServerInfo serverInfo = Deserialize(GetRequiredPropertyFromJson>(properties, JsonRpcStrings.ServerInfo)); ServerCapabilities capabilities = Deserialize(GetRequiredPropertyFromJson>(properties, JsonRpcStrings.Capabilities)); - return new InitializeResponseArgs(serverInfo, capabilities); + return new InitializeResponseArgs(processId, serverInfo, capabilities); }); Deserializers[typeof(ServerInfo)] = new ObjectDeserializer(properties => @@ -561,7 +563,7 @@ static SerializerUtilities() var testsJson = GetOptionalPropertyFromJson(properties, JsonRpcStrings.Tests) as ICollection; - ICollection? tests = testsJson?.OfType>()?.Select(obj => Deserialize(obj)).ToList(); + ICollection? tests = testsJson?.OfType>().Select(obj => Deserialize(obj)).ToList(); string? filter = GetOptionalPropertyFromJson(properties, JsonRpcStrings.Filter) as string; return new RunRequestArgs(runId, tests, filter); @@ -631,7 +633,7 @@ static SerializerUtilities() #if !NETCOREAPP if (errorObj.TryGetValue(JsonRpcStrings.Data, out object? errorData)) { - if (errorData is JsonObject keyValuePairs && keyValuePairs.Count == 0) + if (errorData is JsonObject { Count: 0 }) { errorObj[JsonRpcStrings.Data] = null!; } @@ -658,7 +660,7 @@ static SerializerUtilities() return new ErrorMessage( Id: id, ErrorCode: code, - Message: errorMessage ?? string.Empty, + Message: errorMessage, Data: data); }); } @@ -680,7 +682,7 @@ public static object Deserialize(Type t, IDictionary properties => Serialize(typeof(T), obj!); public static IDictionary SerializeObject(object obj) - => Serialize(obj.GetType(), obj!); + => Serialize(obj.GetType(), obj); public static IDictionary Serialize(Type t, object obj) { diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/ServerModeManager.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/ServerModeManager.cs index 3bec4c98f2..d941d87d79 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/ServerModeManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/ServerModeManager.cs @@ -38,20 +38,14 @@ internal IMessageHandlerFactory Build(IServiceProvider serviceProvider) { case JsonRpcTcpServer tcpServerCommunicationProtocol: { - if (!serverPort.HasValue) - { - serverPort = tcpServerCommunicationProtocol.Port; - } + serverPort ??= tcpServerCommunicationProtocol.Port; break; } case JsonRpcTcpServerToSingleClient tcpServerToSingleClientCommunicationProtocol: { - if (!clientPort.HasValue) - { - clientPort = tcpServerToSingleClientCommunicationProtocol.ClientPort; - } + clientPort ??= tcpServerToSingleClientCommunicationProtocol.ClientPort; if (RoslynString.IsNullOrEmpty(clientHostName)) { diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/ServerModePerCallOutputDevice.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/ServerModePerCallOutputDevice.cs index 4210f0eae7..46f26549e2 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/ServerModePerCallOutputDevice.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/ServerModePerCallOutputDevice.cs @@ -21,7 +21,7 @@ internal class ServerModePerCallOutputDevice : IPlatformOutputDevice public Task DisplayAsync(IOutputDeviceDataProducer producer, IOutputDeviceData data) => Task.CompletedTask; - public Task DisplayBannerAsync() => Task.CompletedTask; + public Task DisplayBannerAsync(string? bannerMessage) => Task.CompletedTask; public Task DisplayBeforeSessionStartAsync() => Task.CompletedTask; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/TcpMessageHandler.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/TcpMessageHandler.cs index e48a25ae56..782fddf1e4 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/TcpMessageHandler.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/TcpMessageHandler.cs @@ -23,23 +23,14 @@ internal sealed class TcpMessageHandler( // Client close the connection in an unexpected way catch (Exception ex) { - if (ex is SocketException se) + switch (ex) { - if (se.SocketErrorCode == SocketError.ConnectionReset) - { + case SocketException { SocketErrorCode: SocketError.ConnectionReset }: + case IOException { InnerException: SocketException { SocketErrorCode: SocketError.ConnectionReset } }: return null; - } + default: + throw; } - - if (ex is IOException iOException && iOException.InnerException is SocketException iose) - { - if (iose.SocketErrorCode == SocketError.ConnectionReset) - { - return null; - } - } - - throw; } } diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/TestNodeStateChangeAggregator.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/TestNodeStateChangeAggregator.cs index 3e58ec858d..91278adca4 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/TestNodeStateChangeAggregator.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/TestNodeStateChangeAggregator.cs @@ -20,7 +20,7 @@ internal class TestNodeStateChangeAggregator(Guid runId) // Note: Currently there's no cascading node changes we need to deal with. private readonly List _stateChanges = []; - public Guid RunId { get; private set; } = runId; + public Guid RunId { get; } = runId; public bool HasChanges => _stateChanges.Count > 0; diff --git a/src/Platform/Microsoft.Testing.Platform/Services/CurrentTestApplicationModuleInfo.cs b/src/Platform/Microsoft.Testing.Platform/Services/CurrentTestApplicationModuleInfo.cs index aeb8663343..5053c18b19 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/CurrentTestApplicationModuleInfo.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/CurrentTestApplicationModuleInfo.cs @@ -87,7 +87,10 @@ public string GetCommandLineArguments() return processPath; } #else - => process.GetCurrentProcess().MainModule.FileName; + { + using IProcess currentProcess = process.GetCurrentProcess(); + return currentProcess.MainModule.FileName; + } #endif public ExecutableInfo GetCurrentExecutableInfo() diff --git a/src/Platform/Microsoft.Testing.Platform/Services/IPlatformInformation.cs b/src/Platform/Microsoft.Testing.Platform/Services/IPlatformInformation.cs new file mode 100644 index 0000000000..2a90bf3569 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/Services/IPlatformInformation.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; + +namespace Microsoft.Testing.Platform.Services; + +[Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] +public interface IPlatformInformation +{ + /// + /// Gets the platform's name. + /// + string Name { get; } + + /// + /// Gets the platform's build date. + /// + DateTimeOffset? BuildDate { get; } + + /// + /// Gets the platform's version. + /// + string? Version { get; } + + /// + /// Gets the platform's commit hash. + /// + string? CommitHash { get; } +} + +internal sealed class PlatformInformation : IPlatformInformation +{ + private const char PlusSign = '+'; + private const string BuildTimeAttributeName = "Microsoft.Testing.Platform.Application.BuildTimeUTC"; + + public PlatformInformation() + { + var assembly = Assembly.GetExecutingAssembly(); + if (assembly.GetCustomAttribute() is { } version) + { + string informationalVersion = version.InformationalVersion; + int index = informationalVersion.LastIndexOf(PlusSign); + if (index != -1) + { + Version = informationalVersion[..index]; + CommitHash = informationalVersion[(index + 1)..]; + } + else + { + Version = informationalVersion; + } + } + + AssemblyMetadataAttribute? buildTime = assembly + .GetCustomAttributes() + .FirstOrDefault(x => x.Key == BuildTimeAttributeName); + + if (!RoslynString.IsNullOrEmpty(buildTime?.Value) + && DateTimeOffset.TryParse(buildTime.Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out DateTimeOffset date)) + { + BuildDate = date; + } + } + + public string Name { get; } = ".NET Testing Platform"; + + public DateTimeOffset? BuildDate { get; } + + public string? Version { get; } + + public string? CommitHash { get; } +} diff --git a/src/Platform/Microsoft.Testing.Platform/Services/ServiceProvider.cs b/src/Platform/Microsoft.Testing.Platform/Services/ServiceProvider.cs index 8469e95e92..c39ef4aedd 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/ServiceProvider.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/ServiceProvider.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if !NETCOREAPP -using System.Reflection; -#endif - using System.Globalization; using Microsoft.Testing.Platform.Extensions.TestFramework; @@ -23,8 +19,8 @@ internal sealed class ServiceProvider : IServiceProvider, ICloneable public bool AllowTestAdapterFrameworkRegistration { get; set; } - private static Type[] InternalOnlyExtensions => new[] - { + private static Type[] InternalOnlyExtensions => + [ // TestHost typeof(ITestApplicationLifecycleCallbacks), typeof(IDataConsumer), @@ -32,8 +28,8 @@ internal sealed class ServiceProvider : IServiceProvider, ICloneable // TestHostControllers typeof(ITestHostEnvironmentVariableProvider), - typeof(ITestHostProcessLifetimeHandler), - }; + typeof(ITestHostProcessLifetimeHandler) + ]; public void AddService(object service, bool throwIfSameInstanceExit = true) { @@ -88,7 +84,7 @@ public IEnumerable GetServicesInternal( foreach (object serviceInstance in _services) { #if !NETCOREAPP - if (serviceType.GetTypeInfo().IsAssignableFrom(serviceInstance.GetType())) + if (serviceType.IsAssignableFrom(serviceInstance.GetType())) { yield return serviceInstance; if (stopAtFirst) diff --git a/src/Platform/Microsoft.Testing.Platform/Services/ServiceProviderExtensions.cs b/src/Platform/Microsoft.Testing.Platform/Services/ServiceProviderExtensions.cs index 0e39de4319..899f016a29 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/ServiceProviderExtensions.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/ServiceProviderExtensions.cs @@ -199,4 +199,7 @@ internal static ITestFrameworkCapabilities GetTestFrameworkCapabilities(this ISe internal static CommandLineParseResult GetCommandLineParseResult(this IServiceProvider serviceProvider) => serviceProvider.GetRequiredServiceInternal().CommandLineParseResult; + + internal static IPlatformInformation GetPlatformInformation(this IServiceProvider serviceProvider) + => serviceProvider.GetRequiredServiceInternal(); } diff --git a/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryManager.cs b/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryManager.cs index 46642abd18..4742dd948b 100644 --- a/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryManager.cs @@ -35,17 +35,17 @@ public async Task BuildAsync(ServiceProvider serviceProvide bool isTelemetryOptedOut = !testApplicationOptions.EnableTelemetry; ILogger logger = loggerFactory.CreateLogger(); - await logger.LogInformationAsync($"TestApplicationOptions.EnableTelemetry: {testApplicationOptions.EnableTelemetry}"); + await logger.LogDebugAsync($"TestApplicationOptions.EnableTelemetry: {testApplicationOptions.EnableTelemetry}"); // If the environment variable is not set or is set to 0, telemetry is opted in. IEnvironment environment = serviceProvider.GetEnvironment(); string? telemetryOptOut = environment.GetEnvironmentVariable(EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT); - await logger.LogInformationAsync($"{EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT} environment variable: '{telemetryOptOut}'"); - isTelemetryOptedOut = (telemetryOptOut is not null and ("1" or "true")) || isTelemetryOptedOut; + await logger.LogDebugAsync($"{EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT} environment variable: '{telemetryOptOut}'"); + isTelemetryOptedOut = (telemetryOptOut is "1" or "true") || isTelemetryOptedOut; string? cli_telemetryOptOut = environment.GetEnvironmentVariable(EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT); - await logger.LogInformationAsync($"{EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT} environment variable: '{cli_telemetryOptOut}'"); - isTelemetryOptedOut = (cli_telemetryOptOut is not null and ("1" or "true")) || isTelemetryOptedOut; + await logger.LogDebugAsync($"{EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT} environment variable: '{cli_telemetryOptOut}'"); + isTelemetryOptedOut = (cli_telemetryOptOut is "1" or "true") || isTelemetryOptedOut; // NO_LOGO @@ -54,14 +54,14 @@ public async Task BuildAsync(ServiceProvider serviceProvide bool doNotShowLogo = commandLineOptions.IsOptionSet(PlatformCommandLineProvider.NoBannerOptionKey); string? noBannerEnvVar = environment.GetEnvironmentVariable(EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER); - await logger.LogInformationAsync($"{EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER} environment variable: '{noBannerEnvVar}'"); - doNotShowLogo = (noBannerEnvVar is not null and ("1" or "true")) || doNotShowLogo; + await logger.LogDebugAsync($"{EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER} environment variable: '{noBannerEnvVar}'"); + doNotShowLogo = (noBannerEnvVar is "1" or "true") || doNotShowLogo; string? dotnetNoLogoEnvVar = environment.GetEnvironmentVariable(EnvironmentVariableConstants.DOTNET_NOLOGO); - await logger.LogInformationAsync($"{EnvironmentVariableConstants.DOTNET_NOLOGO} environment variable: '{dotnetNoLogoEnvVar}'"); - doNotShowLogo = (dotnetNoLogoEnvVar is not null and ("1" or "true")) || doNotShowLogo; + await logger.LogDebugAsync($"{EnvironmentVariableConstants.DOTNET_NOLOGO} environment variable: '{dotnetNoLogoEnvVar}'"); + doNotShowLogo = (dotnetNoLogoEnvVar is "1" or "true") || doNotShowLogo; - await logger.LogInformationAsync($"Telemetry is '{(!isTelemetryOptedOut ? "ENABLED" : "DISABLED")}'"); + await logger.LogDebugAsync($"Telemetry is '{(!isTelemetryOptedOut ? "ENABLED" : "DISABLED")}'"); if (!isTelemetryOptedOut && !doNotShowLogo) { @@ -115,7 +115,7 @@ public async Task BuildAsync(ServiceProvider serviceProvide if (!isTelemetryOptedOut) { - await logger.LogInformationAsync($"Telemetry collector provider: '{telemetryCollector.GetType()}'"); + await logger.LogDebugAsync($"Telemetry collector provider: '{telemetryCollector.GetType()}'"); } return telemetryCollector; diff --git a/src/Platform/Microsoft.Testing.Platform/TestFramework/ITestFrameworkManager.cs b/src/Platform/Microsoft.Testing.Platform/TestFramework/ITestFrameworkManager.cs index 529d6cde26..adc76697ff 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestFramework/ITestFrameworkManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestFramework/ITestFrameworkManager.cs @@ -8,7 +8,7 @@ namespace Microsoft.Testing.Internal.Framework; internal interface ITestFrameworkManager { - Func TestFrameworkAdapterFactory { get; } + Func TestFrameworkFactory { get; } Func TestFrameworkCapabilitiesFactory { get; } } diff --git a/src/Platform/Microsoft.Testing.Platform/TestFramework/TestFrameworkManager.cs b/src/Platform/Microsoft.Testing.Platform/TestFramework/TestFrameworkManager.cs index 9ae0f6c812..6027b14792 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestFramework/TestFrameworkManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestFramework/TestFrameworkManager.cs @@ -7,11 +7,11 @@ namespace Microsoft.Testing.Internal.Framework; internal class TestFrameworkManager( - Func testFrameworkAdapterFactory, + Func testFrameworkFactory, Func testFrameworkCapabilitiesFactory) : ITestFrameworkManager { - public Func TestFrameworkAdapterFactory { get; } = testFrameworkAdapterFactory; + public Func TestFrameworkFactory { get; } = testFrameworkFactory; public Func TestFrameworkCapabilitiesFactory { get; } = testFrameworkCapabilitiesFactory; } diff --git a/src/Platform/Microsoft.Testing.Platform/TestHost/ITestHostExtension.cs b/src/Platform/Microsoft.Testing.Platform/TestHost/ITestHostExtension.cs index 2bfdafc831..3164bcf364 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHost/ITestHostExtension.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHost/ITestHostExtension.cs @@ -6,6 +6,4 @@ namespace Microsoft.Testing.Platform.Extensions.TestHost; /// /// Represents a test host specialized extension. /// -public interface ITestHostExtension : IExtension -{ -} +public interface ITestHostExtension : IExtension; diff --git a/src/Platform/Microsoft.Testing.Platform/TestHost/TestHostManager.cs b/src/Platform/Microsoft.Testing.Platform/TestHost/TestHostManager.cs index f8b5b9edaa..1b579f5ad5 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHost/TestHostManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHost/TestHostManager.cs @@ -51,10 +51,7 @@ internal async Task> TryBuildTestAdapterInvo // We initialize only if enabled if (await testAdapterInvoke.IsEnabledAsync()) { - if (testAdapterInvoke is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await testAdapterInvoke.TryInitializeAsync(); return ActionResult.Ok(testAdapterInvoke); } @@ -85,10 +82,7 @@ internal async Task> TryBuildTestExecu // We initialize only if enabled if (await testExecutionFilterFactory.IsEnabledAsync()) { - if (testExecutionFilterFactory is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await testExecutionFilterFactory.TryInitializeAsync(); return ActionResult.Ok(testExecutionFilterFactory); } @@ -119,10 +113,7 @@ internal async Task BuildTestApplicationLi // We initialize only if enabled if (await service.IsEnabledAsync()) { - if (service is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await service.TryInitializeAsync(); // Register the extension for usage testApplicationLifecycleCallbacks.Add(service); @@ -169,10 +160,7 @@ public void AddDataConsumer(CompositeExtensionFactory compositeServiceFact // We initialize only if enabled if (await service.IsEnabledAsync()) { - if (service is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await service.TryInitializeAsync(); // Register the extension for usage dataConsumers.Add((service, _factoryOrdering.IndexOf(dataConsumerFactory))); @@ -202,10 +190,7 @@ public void AddDataConsumer(CompositeExtensionFactory compositeServiceFact // We initialize only if enabled if (await instance.IsEnabledAsync()) { - if (instance is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await instance.TryInitializeAsync(); } // Add to the list of shared singletons @@ -269,10 +254,7 @@ public void AddTestSessionLifetimeHandle(CompositeExtensionFactory composi // We initialize only if enabled if (await service.IsEnabledAsync()) { - if (service is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await service.TryInitializeAsync(); // Register the extension for usage testSessionLifetimeHandlers.Add((service, _factoryOrdering.IndexOf(testSessionLifetimeHandlerFactory))); @@ -302,10 +284,7 @@ public void AddTestSessionLifetimeHandle(CompositeExtensionFactory composi // We initialize only if enabled if (await instance.IsEnabledAsync()) { - if (instance is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await instance.TryInitializeAsync(); } // Add to the list of shared singletons diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/CommandLineInfo.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/CommandLineInfo.cs deleted file mode 100644 index a367857906..0000000000 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/CommandLineInfo.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -namespace Microsoft.Testing.Platform.Extensions.TestHostControllers; - -internal sealed class CommandLineInfo(string fileName, IEnumerable arguments, string testApplicationFullPath) -{ - public string FileName { get; } = fileName; - - public IEnumerable Arguments { get; } = arguments; - - public string TestApplicationFullPath { get; } = testApplicationFullPath; -} diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/ITestHostControllersExtension.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/ITestHostControllersExtension.cs index 8c73a4954e..054d08093c 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/ITestHostControllersExtension.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/ITestHostControllersExtension.cs @@ -6,6 +6,4 @@ namespace Microsoft.Testing.Platform.Extensions.TestHostControllers; /// /// Represents an extension for test host controllers. /// -public interface ITestHostControllersExtension : IExtension -{ -} +public interface ITestHostControllersExtension : IExtension; diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/SystemEnvironmentVariableProvider.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/SystemEnvironmentVariableProvider.cs index 501c87c046..8e7021df5c 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/SystemEnvironmentVariableProvider.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/SystemEnvironmentVariableProvider.cs @@ -28,7 +28,7 @@ public Task UpdateAsync(IEnvironmentVariables environmentVariables) { foreach (DictionaryEntry entry in _environment.GetEnvironmentVariables()) { - environmentVariables.SetVariable(new(entry.Key!.ToString()!, entry.Value!.ToString(), false, false)); + environmentVariables.SetVariable(new(entry.Key.ToString()!, entry.Value!.ToString(), false, false)); } return Task.CompletedTask; diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllersManager.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllersManager.cs index f88b995dfd..d6ca7c69c2 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllersManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllersManager.cs @@ -111,10 +111,7 @@ internal async Task BuildAsync(ServiceProvider // We initialize only if enabled if (await envVarProvider.IsEnabledAsync()) { - if (envVarProvider is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await envVarProvider.TryInitializeAsync(); // Register the extension for usage environmentVariableProviders.Add((envVarProvider, _factoryOrdering.IndexOf(environmentVariableProviderFactory))); @@ -140,10 +137,7 @@ internal async Task BuildAsync(ServiceProvider // We initialize only if enabled if (await instance.IsEnabledAsync()) { - if (instance is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await instance.TryInitializeAsync(); } // Add to the list of shared singletons @@ -184,10 +178,7 @@ internal async Task BuildAsync(ServiceProvider // We initialize only if enabled if (await lifetimeHandler.IsEnabledAsync()) { - if (lifetimeHandler is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await lifetimeHandler.TryInitializeAsync(); // Register the extension for usage lifetimeHandlers.Add((lifetimeHandler, _factoryOrdering.IndexOf(lifetimeHandlerFactory))); @@ -212,10 +203,7 @@ internal async Task BuildAsync(ServiceProvider // We initialize only if enabled if (await instance.IsEnabledAsync()) { - if (instance is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await instance.TryInitializeAsync(); } // Add to the list of shared singletons @@ -256,10 +244,7 @@ internal async Task BuildAsync(ServiceProvider // We initialize only if enabled if (await service.IsEnabledAsync()) { - if (service is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await service.TryInitializeAsync(); // Register the extension for usage dataConsumers.Add((service, _factoryOrdering.IndexOf(dataConsumerFactory))); @@ -289,10 +274,7 @@ internal async Task BuildAsync(ServiceProvider // We initialize only if enabled if (await instance.IsEnabledAsync()) { - if (instance is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await instance.TryInitializeAsync(); } // Add to the list of shared singletons diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/TestHostOrchestratorManager.cs b/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/TestHostOrchestratorManager.cs index 404bef5833..acd8a53208 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/TestHostOrchestratorManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/TestHostOrchestratorManager.cs @@ -42,10 +42,7 @@ public async Task BuildAsync(ServiceProvider // We initialize only if enabled if (await orchestrator.IsEnabledAsync()) { - if (orchestrator is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await orchestrator.TryInitializeAsync(); // Register the extension for usage orchestrators.Add(orchestrator); diff --git a/src/Platform/Microsoft.Testing.Platform/Tools/ToolsManager.cs b/src/Platform/Microsoft.Testing.Platform/Tools/ToolsManager.cs index 9d556510b5..63d96833c8 100644 --- a/src/Platform/Microsoft.Testing.Platform/Tools/ToolsManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/Tools/ToolsManager.cs @@ -27,10 +27,7 @@ internal async Task BuildAsync(IServiceProvider serviceProvide continue; } - if (tool is IAsyncInitializableExtension async) - { - await async.InitializeAsync(); - } + await tool.TryInitializeAsync(); tools.Add(tool); } diff --git a/src/TestFramework/TestFramework.Extensions/Attributes/WinUI_UITestMethodAttribute.cs b/src/TestFramework/TestFramework.Extensions/Attributes/WinUI_UITestMethodAttribute.cs index 478632ab08..ec26f36df5 100644 --- a/src/TestFramework/TestFramework.Extensions/Attributes/WinUI_UITestMethodAttribute.cs +++ b/src/TestFramework/TestFramework.Extensions/Attributes/WinUI_UITestMethodAttribute.cs @@ -87,7 +87,7 @@ public override TestResult[] Execute(ITestMethod testMethod) { try { - result = testMethod.Invoke(Array.Empty()); + result = testMethod.Invoke([]); taskCompletionSource.SetResult(null); } catch (Exception e) diff --git a/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/DataSourceElement.cs b/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/DataSourceElement.cs index 3905ec9daa..1d630e02b8 100644 --- a/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/DataSourceElement.cs +++ b/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/DataSourceElement.cs @@ -17,13 +17,13 @@ public sealed class DataSourceElement : ConfigurationElement private static readonly ConfigurationProperty DataTableNameValue = new(ConfigurationNames.DataTableAttributeName, typeof(string), string.Empty, ConfigurationPropertyOptions.IsRequired); private static readonly ConfigurationProperty DataAccessMethodValue = new(ConfigurationNames.DataAccessMethodAttributeName, typeof(string), string.Empty); - private static readonly ConfigurationPropertyCollection SharedProperties = new() - { + private static readonly ConfigurationPropertyCollection SharedProperties = + [ NameValue, ConnectionStringValue, DataAccessMethodValue, - DataTableNameValue, - }; + DataTableNameValue + ]; /// /// Gets or sets the name of this configuration. diff --git a/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/TestConfigurationSection.cs b/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/TestConfigurationSection.cs index 54fb92147f..91fde548cd 100644 --- a/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/TestConfigurationSection.cs +++ b/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/TestConfigurationSection.cs @@ -26,6 +26,6 @@ public sealed class TestConfigurationSection : ConfigurationSection /// /// The of properties for the element. /// - protected override ConfigurationPropertyCollection Properties { get; } = new() { DataSourcesValue }; + protected override ConfigurationPropertyCollection Properties { get; } = [DataSourcesValue]; } #endif diff --git a/src/TestFramework/TestFramework.Extensions/PrivateObject.cs b/src/TestFramework/TestFramework.Extensions/PrivateObject.cs index d380100741..be4a1e88bf 100644 --- a/src/TestFramework/TestFramework.Extensions/PrivateObject.cs +++ b/src/TestFramework/TestFramework.Extensions/PrivateObject.cs @@ -692,7 +692,7 @@ private void BuildGenericMethodCacheForType(Type t) foreach (MethodInfo member in members) { - if (!member.IsGenericMethod && !member.IsGenericMethodDefinition) + if (member is { IsGenericMethod: false, IsGenericMethodDefinition: false }) { continue; } diff --git a/src/TestFramework/TestFramework.Extensions/PrivateType.cs b/src/TestFramework/TestFramework.Extensions/PrivateType.cs index 4c62bc75b1..8b950feed3 100644 --- a/src/TestFramework/TestFramework.Extensions/PrivateType.cs +++ b/src/TestFramework/TestFramework.Extensions/PrivateType.cs @@ -285,7 +285,7 @@ public object GetStaticField(string name, BindingFlags bindingFlags) public void SetStaticField(string name, BindingFlags bindingFlags, object value) { _ = name ?? throw new ArgumentNullException(nameof(name)); - InvokeHelperStatic(name, BindingFlags.SetField | bindingFlags | BindingFlags.Static, new[] { value }, CultureInfo.InvariantCulture); + InvokeHelperStatic(name, BindingFlags.SetField | bindingFlags | BindingFlags.Static, [value], CultureInfo.InvariantCulture); } /// @@ -331,7 +331,7 @@ public object GetStaticFieldOrProperty(string name, BindingFlags bindingFlags) public void SetStaticFieldOrProperty(string name, BindingFlags bindingFlags, object value) { _ = name ?? throw new ArgumentNullException(nameof(name)); - InvokeHelperStatic(name, BindingFlags.SetField | BindingFlags.SetProperty | bindingFlags | BindingFlags.Static, new[] { value }, CultureInfo.InvariantCulture); + InvokeHelperStatic(name, BindingFlags.SetField | BindingFlags.SetProperty | bindingFlags | BindingFlags.Static, [value], CultureInfo.InvariantCulture); } /// diff --git a/src/TestFramework/TestFramework.Extensions/RuntimeTypeHelper.cs b/src/TestFramework/TestFramework.Extensions/RuntimeTypeHelper.cs index 7328b5fbd9..8216d0ef4e 100644 --- a/src/TestFramework/TestFramework.Extensions/RuntimeTypeHelper.cs +++ b/src/TestFramework/TestFramework.Extensions/RuntimeTypeHelper.cs @@ -335,8 +335,7 @@ internal static int FindMostSpecific( // in which case paramOrder[i] == p1.Length - 1 for that element // or the user did not re-order the parameters in which case // the paramOrder array could contain indexes larger than p.Length - 1 - //// so any index >= p.Length - 1 is being put in the param array - + // so any index >= p.Length - 1 is being put in the param array c1 = paramArrayType1 != null && paramOrder1[i] >= p1.Length - 1 ? paramArrayType1 : p1[paramOrder1[i]].ParameterType; c2 = paramArrayType2 != null && paramOrder2[i] >= p2.Length - 1 ? paramArrayType2 : p2[paramOrder2[i]].ParameterType; diff --git a/src/TestFramework/TestFramework.Extensions/TestFramework.Extensions.csproj b/src/TestFramework/TestFramework.Extensions/TestFramework.Extensions.csproj index 752ab6ec67..450d863049 100644 --- a/src/TestFramework/TestFramework.Extensions/TestFramework.Extensions.csproj +++ b/src/TestFramework/TestFramework.Extensions/TestFramework.Extensions.csproj @@ -5,7 +5,7 @@ - $(UwpMinimum);$(WinUiMinimum);netstandard2.0;$(NetFrameworkMinimum);$(SupportedNetFrameworks) + netstandard2.0;$(NetFrameworkMinimum);$(SupportedNetFrameworks);$(UwpMinimum);$(WinUiMinimum) $(SupportedNetFrameworks);netstandard2.0 enable true diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs index cc8590ede1..b8b2831400 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Diagnostics.CodeAnalysis; @@ -139,7 +139,7 @@ public static void AreEqual(T? expected, T? actual, IEqualityComparer? com /// Thrown if is not equal to /// . /// - public static void AreEqual(T? expected, T? actual, string? message, params object?[]? parameters) + public static void AreEqual(T? expected, T? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => AreEqual(expected, actual, null, message, parameters); /// @@ -173,7 +173,7 @@ public static void AreEqual(T? expected, T? actual, string? message, params o /// . /// public static void AreEqual(T? expected, T? actual, IEqualityComparer? comparer, - string? message, params object?[]? parameters) + [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { IEqualityComparer localComparer = comparer ?? EqualityComparer.Default; if (localComparer.Equals(expected!, actual!)) @@ -273,8 +273,13 @@ public static void AreEqual(IEquatable? expected, IEquatable? actual, s /// Thrown if is not equal to /// . /// - public static void AreEqual(IEquatable? expected, IEquatable? actual, string? message, params object?[]? parameters) + public static void AreEqual(IEquatable? expected, IEquatable? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { + if (actual is null && expected is null) + { + return; + } + if (actual?.Equals(expected) == true) { return; @@ -428,7 +433,7 @@ public static void AreNotEqual(T? notExpected, T? actual, IEqualityComparer /// Thrown if is equal to . /// - public static void AreNotEqual(T? notExpected, T? actual, string? message, params object?[]? parameters) + public static void AreNotEqual(T? notExpected, T? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => AreNotEqual(notExpected, actual, null, message, parameters); /// @@ -462,7 +467,7 @@ public static void AreNotEqual(T? notExpected, T? actual, string? message, pa /// Thrown if is equal to . /// public static void AreNotEqual(T? notExpected, T? actual, IEqualityComparer? comparer, - string? message, params object?[]? parameters) + [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { IEqualityComparer localComparer = comparer ?? EqualityComparer.Default; if (!localComparer.Equals(notExpected!, actual!)) @@ -556,7 +561,7 @@ public static void AreEqual(float expected, float actual, float delta, string? m /// Thrown if is not equal to /// . /// - public static void AreEqual(float expected, float actual, float delta, string? message, + public static void AreEqual(float expected, float actual, float delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (float.IsNaN(expected) || float.IsNaN(actual) || float.IsNaN(delta)) @@ -662,7 +667,7 @@ public static void AreNotEqual(float notExpected, float actual, float delta, str /// /// Thrown if is equal to . /// - public static void AreNotEqual(float notExpected, float actual, float delta, string? message, + public static void AreNotEqual(float notExpected, float actual, float delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (Math.Abs(notExpected - actual) <= delta) @@ -755,7 +760,7 @@ public static void AreEqual(decimal expected, decimal actual, decimal delta, str /// Thrown if is not equal to /// . /// - public static void AreEqual(decimal expected, decimal actual, decimal delta, string? message, + public static void AreEqual(decimal expected, decimal actual, decimal delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (Math.Abs(expected - actual) > delta) @@ -848,7 +853,7 @@ public static void AreNotEqual(decimal notExpected, decimal actual, decimal delt /// /// Thrown if is equal to . /// - public static void AreNotEqual(decimal notExpected, decimal actual, decimal delta, string? message, + public static void AreNotEqual(decimal notExpected, decimal actual, decimal delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (Math.Abs(notExpected - actual) <= delta) @@ -941,7 +946,7 @@ public static void AreEqual(long expected, long actual, long delta, string? mess /// Thrown if is not equal to /// . /// - public static void AreEqual(long expected, long actual, long delta, string? message, + public static void AreEqual(long expected, long actual, long delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (Math.Abs(expected - actual) > delta) @@ -1034,7 +1039,7 @@ public static void AreNotEqual(long notExpected, long actual, long delta, string /// /// Thrown if is equal to . /// - public static void AreNotEqual(long notExpected, long actual, long delta, string? message, + public static void AreNotEqual(long notExpected, long actual, long delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (Math.Abs(notExpected - actual) <= delta) @@ -1125,7 +1130,7 @@ public static void AreEqual(double expected, double actual, double delta, string /// /// Thrown if is not equal to . /// - public static void AreEqual(double expected, double actual, double delta, string? message, + public static void AreEqual(double expected, double actual, double delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (double.IsNaN(expected) || double.IsNaN(actual) || double.IsNaN(delta)) @@ -1231,7 +1236,7 @@ public static void AreNotEqual(double notExpected, double actual, double delta, /// /// Thrown if is equal to . /// - public static void AreNotEqual(double notExpected, double actual, double delta, string? message, + public static void AreNotEqual(double notExpected, double actual, double delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (Math.Abs(notExpected - actual) <= delta) @@ -1318,7 +1323,7 @@ public static void AreEqual(string? expected, string? actual, bool ignoreCase, s /// /// Thrown if is not equal to . /// - public static void AreEqual(string? expected, string? actual, bool ignoreCase, string? message, + public static void AreEqual(string? expected, string? actual, bool ignoreCase, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => AreEqual(expected, actual, ignoreCase, CultureInfo.InvariantCulture, message, parameters); @@ -1404,7 +1409,7 @@ public static void AreEqual(string? expected, string? actual, bool ignoreCase, /// Thrown if is not equal to . /// public static void AreEqual(string? expected, string? actual, bool ignoreCase, - [NotNull] CultureInfo? culture, string? message, params object?[]? parameters) + [NotNull] CultureInfo? culture, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { CheckParameterNotNull(culture, "Assert.AreEqual", "culture", string.Empty); if (CompareInternal(expected, actual, ignoreCase, culture) == 0) @@ -1505,7 +1510,7 @@ public static void AreNotEqual(string? notExpected, string? actual, bool ignoreC /// Thrown if is equal to . /// public static void AreNotEqual(string? notExpected, string? actual, bool ignoreCase, - string? message, params object?[]? parameters) + [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => AreNotEqual(notExpected, actual, ignoreCase, CultureInfo.InvariantCulture, message, parameters); /// @@ -1592,7 +1597,7 @@ public static void AreNotEqual(string? notExpected, string? actual, bool ignoreC /// Thrown if is equal to . /// public static void AreNotEqual(string? notExpected, string? actual, bool ignoreCase, - CultureInfo? culture, string? message, params object?[]? parameters) + CultureInfo? culture, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { CheckParameterNotNull(culture, "Assert.AreNotEqual", "culture", string.Empty); if (CompareInternal(notExpected, actual, ignoreCase, culture) != 0) diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs index d86f58d5f4..07115c97d1 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs @@ -1,6 +1,7 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace Microsoft.VisualStudio.TestTools.UnitTesting; @@ -82,7 +83,7 @@ public static void AreSame(T? expected, T? actual, string? message) /// Thrown if does not refer to the same object /// as . /// - public static void AreSame(T? expected, T? actual, string? message, params object?[]? parameters) + public static void AreSame(T? expected, T? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (ReferenceEquals(expected, actual)) { @@ -179,7 +180,7 @@ public static void AreNotSame(T? notExpected, T? actual, string? message) /// Thrown if refers to the same object /// as . /// - public static void AreNotSame(T? notExpected, T? actual, string? message, params object?[]? parameters) + public static void AreNotSame(T? notExpected, T? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (ReferenceEquals(notExpected, actual)) { diff --git a/src/TestFramework/TestFramework/Assertions/Assert.Fail.cs b/src/TestFramework/TestFramework/Assertions/Assert.Fail.cs index fe55ee3a15..ffb3ee17ff 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.Fail.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.Fail.cs @@ -50,6 +50,6 @@ public static void Fail(string? message) /// Always thrown. /// [DoesNotReturn] - public static void Fail(string? message, params object?[]? parameters) + public static void Fail([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => ThrowAssertFailed("Assert.Fail", BuildUserMessage(message, parameters)); } diff --git a/src/TestFramework/TestFramework/Assertions/Assert.Inconclusive.cs b/src/TestFramework/TestFramework/Assertions/Assert.Inconclusive.cs index d5d1456c44..63766dfc51 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.Inconclusive.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.Inconclusive.cs @@ -51,7 +51,7 @@ public static void Inconclusive(string? message) /// Always thrown. /// [DoesNotReturn] - public static void Inconclusive(string? message, params object?[]? parameters) + public static void Inconclusive([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { string userMessage = BuildUserMessage(message, parameters); throw new AssertInconclusiveException( diff --git a/src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs b/src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs index 836ab7db14..e2d7cf5cef 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs @@ -117,7 +117,7 @@ public static void IsInstanceOfType([NotNull] object? value, out T instance, /// is not in the inheritance hierarchy /// of . /// - public static void IsInstanceOfType([NotNull] object? value, [NotNull] Type? expectedType, string? message, + public static void IsInstanceOfType([NotNull] object? value, [NotNull] Type? expectedType, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (expectedType == null || value == null) @@ -146,7 +146,7 @@ public static void IsInstanceOfType([NotNull] object? value, [NotNull] Type? exp /// inheritance hierarchy of the object. /// /// The expected type of . - public static void IsInstanceOfType([NotNull] object? value, string? message, params object?[]? parameters) + public static void IsInstanceOfType([NotNull] object? value, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => IsInstanceOfType(value, typeof(T), message, parameters); /// @@ -155,7 +155,7 @@ public static void IsInstanceOfType([NotNull] object? value, string? message, /// inheritance hierarchy of the object. /// /// The expected type of . - public static void IsInstanceOfType([NotNull] object? value, out T instance, string? message, params object?[]? parameters) + public static void IsInstanceOfType([NotNull] object? value, out T instance, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { IsInstanceOfType(value, typeof(T), message, parameters); instance = (T)value; @@ -246,7 +246,7 @@ public static void IsNotInstanceOfType(object? value, string? message) /// is in the inheritance hierarchy /// of . /// - public static void IsNotInstanceOfType(object? value, [NotNull] Type? wrongType, string? message, + public static void IsNotInstanceOfType(object? value, [NotNull] Type? wrongType, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (wrongType == null) @@ -281,6 +281,6 @@ public static void IsNotInstanceOfType(object? value, [NotNull] Type? wrongType, /// inheritance hierarchy of the object. /// /// The type that should not be. - public static void IsNotInstanceOfType(object? value, string? message, params object?[]? parameters) + public static void IsNotInstanceOfType(object? value, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => IsNotInstanceOfType(value, typeof(T), message, parameters); } diff --git a/src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs b/src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs index 235f4b8ca7..c809925a0f 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs @@ -59,7 +59,7 @@ public static void IsNull(object? value, string? message) /// /// Thrown if is not null. /// - public static void IsNull(object? value, string? message, params object?[]? parameters) + public static void IsNull(object? value, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (value != null) { @@ -114,7 +114,7 @@ public static void IsNotNull([NotNull] object? value, string? message) /// /// Thrown if is null. /// - public static void IsNotNull([NotNull] object? value, string? message, params object?[]? parameters) + public static void IsNotNull([NotNull] object? value, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (value == null) { diff --git a/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs b/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs index 8305f42c97..6aa4bea21c 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs @@ -89,7 +89,7 @@ public static void IsTrue([DoesNotReturnIf(false)] bool? condition, string? mess /// /// Thrown if is false. /// - public static void IsTrue([DoesNotReturnIf(false)] bool condition, string? message, + public static void IsTrue([DoesNotReturnIf(false)] bool condition, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (!condition) @@ -115,7 +115,7 @@ public static void IsTrue([DoesNotReturnIf(false)] bool condition, string? messa /// /// Thrown if is false. /// - public static void IsTrue([DoesNotReturnIf(false)] bool? condition, string? message, + public static void IsTrue([DoesNotReturnIf(false)] bool? condition, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (condition is false or null) @@ -201,7 +201,7 @@ public static void IsFalse([DoesNotReturnIf(true)] bool? condition, string? mess /// /// Thrown if is true. /// - public static void IsFalse([DoesNotReturnIf(true)] bool condition, string? message, + public static void IsFalse([DoesNotReturnIf(true)] bool condition, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (condition) @@ -227,7 +227,7 @@ public static void IsFalse([DoesNotReturnIf(true)] bool condition, string? messa /// /// Thrown if is true. /// - public static void IsFalse([DoesNotReturnIf(true)] bool? condition, string? message, + public static void IsFalse([DoesNotReturnIf(true)] bool? condition, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (condition is true or null) diff --git a/src/TestFramework/TestFramework/Assertions/Assert.cs b/src/TestFramework/TestFramework/Assertions/Assert.cs index 50068a9de2..4a35cbe508 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.cs @@ -100,7 +100,7 @@ internal static string BuildUserMessage(string? format, params object?[]? parame /// The parameters. /// internal static void CheckParameterNotNull([NotNull] object? param, string assertionName, string parameterName, - string? message, params object?[]? parameters) + [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { if (param == null) { diff --git a/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs b/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs index f4708b2f5b..2d5b75d2ad 100644 --- a/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs +++ b/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections; @@ -97,7 +97,7 @@ public static void Contains([NotNull] ICollection? collection, object? element, /// is null, or does not contain /// element . /// - public static void Contains([NotNull] ICollection? collection, object? element, string? message, + public static void Contains([NotNull] ICollection? collection, object? element, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { Assert.CheckParameterNotNull(collection, "CollectionAssert.Contains", "collection", string.Empty); @@ -174,7 +174,7 @@ public static void DoesNotContain([NotNull] ICollection? collection, object? ele /// is null, or contains /// element . /// - public static void DoesNotContain([NotNull] ICollection? collection, object? element, string? message, + public static void DoesNotContain([NotNull] ICollection? collection, object? element, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { Assert.CheckParameterNotNull(collection, "CollectionAssert.DoesNotContain", "collection", string.Empty); @@ -235,7 +235,7 @@ public static void AllItemsAreNotNull([NotNull] ICollection? collection, string? /// /// is null, or contains a null element. /// - public static void AllItemsAreNotNull([NotNull] ICollection? collection, string? message, + public static void AllItemsAreNotNull([NotNull] ICollection? collection, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { Assert.CheckParameterNotNull(collection, "CollectionAssert.AllItemsAreNotNull", "collection", string.Empty); @@ -300,7 +300,7 @@ public static void AllItemsAreUnique([NotNull] ICollection? collection, string? /// is null, or contains at least one duplicate /// element. /// - public static void AllItemsAreUnique([NotNull] ICollection? collection, string? message, + public static void AllItemsAreUnique([NotNull] ICollection? collection, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { Assert.CheckParameterNotNull(collection, "CollectionAssert.AllItemsAreUnique", "collection", string.Empty); @@ -424,7 +424,7 @@ public static void IsSubsetOf([NotNull] ICollection? subset, [NotNull] ICollecti /// or contains at least one element not contained in /// . /// - public static void IsSubsetOf([NotNull] ICollection? subset, [NotNull] ICollection? superset, string? message, + public static void IsSubsetOf([NotNull] ICollection? subset, [NotNull] ICollection? superset, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { Assert.CheckParameterNotNull(subset, "CollectionAssert.IsSubsetOf", "subset", string.Empty); @@ -499,7 +499,7 @@ public static void IsNotSubsetOf([NotNull] ICollection? subset, [NotNull] IColle /// is null, or is null, /// or all elements of are contained in . /// - public static void IsNotSubsetOf([NotNull] ICollection? subset, [NotNull] ICollection? superset, string? message, + public static void IsNotSubsetOf([NotNull] ICollection? subset, [NotNull] ICollection? superset, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { Assert.CheckParameterNotNull(subset, "CollectionAssert.IsNotSubsetOf", "subset", string.Empty); @@ -533,7 +533,7 @@ public static void IsNotSubsetOf([NotNull] ICollection? subset, [NotNull] IColle /// public static void AreEquivalent( [NotNullIfNotNull(nameof(actual))] ICollection? expected, [NotNullIfNotNull(nameof(expected))] ICollection? actual) - => AreEquivalent(expected, actual, string.Empty, null); + => AreEquivalent(expected?.Cast(), actual?.Cast(), EqualityComparer.Default, string.Empty, null); /// /// Tests whether two collections contain the same elements and throws an @@ -560,7 +560,7 @@ public static void AreEquivalent( public static void AreEquivalent( [NotNullIfNotNull(nameof(actual))] ICollection? expected, [NotNullIfNotNull(nameof(expected))] ICollection? actual, string? message) - => AreEquivalent(expected, actual, message, null); + => AreEquivalent(expected?.Cast(), actual?.Cast(), EqualityComparer.Default, message, null); /// /// Tests whether two collections contain the same elements and throws an @@ -589,8 +589,106 @@ public static void AreEquivalent( /// public static void AreEquivalent( [NotNullIfNotNull(nameof(actual))] ICollection? expected, [NotNullIfNotNull(nameof(expected))] ICollection? actual, - string? message, params object?[]? parameters) + [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) + => AreEquivalent(expected?.Cast(), actual?.Cast(), EqualityComparer.Default, message, parameters); + + /// + /// Tests whether two collections contain the same elements and throws an + /// exception if either collection contains an element not in the other + /// collection. + /// + /// + /// The type of values to compare. + /// + /// + /// The first collection to compare. This contains the elements the test + /// expects. + /// + /// + /// The second collection to compare. This is the collection produced by + /// the code under test. + /// + /// + /// The compare implementation to use when comparing elements of the collection. + /// + /// + /// and nullabilities don't match, + /// or if any element was found in one of the collections but not the other. + /// + public static void AreEquivalent( + [NotNullIfNotNull(nameof(actual))] IEnumerable? expected, [NotNullIfNotNull(nameof(expected))] IEnumerable? actual, [NotNull] IEqualityComparer? comparer) + => AreEquivalent(expected, actual, comparer, string.Empty, null); + + /// + /// Tests whether two collections contain the same elements and throws an + /// exception if either collection contains an element not in the other + /// collection. + /// + /// + /// The type of values to compare. + /// + /// + /// The first collection to compare. This contains the elements the test + /// expects. + /// + /// + /// The second collection to compare. This is the collection produced by + /// the code under test. + /// + /// + /// The compare implementation to use when comparing elements of the collection. + /// + /// + /// The message to include in the exception when an element was found + /// in one of the collections but not the other. The message is shown + /// in test results. + /// + /// + /// and nullabilities don't match, + /// or if any element was found in one of the collections but not the other. + /// + public static void AreEquivalent( + [NotNullIfNotNull(nameof(actual))] IEnumerable? expected, [NotNullIfNotNull(nameof(expected))] IEnumerable? actual, [NotNull] IEqualityComparer? comparer, + string? message) + => AreEquivalent(expected, actual, comparer, message, null); + + /// + /// Tests whether two collections contain the same elements and throws an + /// exception if either collection contains an element not in the other + /// collection. + /// + /// + /// The type of values to compare. + /// + /// + /// The first collection to compare. This contains the elements the test + /// expects. + /// + /// + /// The second collection to compare. This is the collection produced by + /// the code under test. + /// + /// + /// The compare implementation to use when comparing elements of the collection. + /// + /// + /// The message to include in the exception when an element was found + /// in one of the collections but not the other. The message is shown + /// in test results. + /// + /// + /// An array of parameters to use when formatting . + /// + /// + /// and nullabilities don't match, + /// or if any element was found in one of the collections but not the other. + /// + public static void AreEquivalent( + [NotNullIfNotNull(nameof(actual))] IEnumerable? expected, [NotNullIfNotNull(nameof(expected))] IEnumerable? actual, [NotNull] IEqualityComparer? comparer, + [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { + Assert.CheckParameterNotNull(comparer, "Assert.AreCollectionsEqual", "comparer", string.Empty); + // Check whether one is null while the other is not. if (expected == null != (actual == null)) { @@ -603,27 +701,32 @@ public static void AreEquivalent( return; } + DebugEx.Assert(actual is not null, "actual is not null here"); + + int expectedCollectionCount = expected.Count(); + int actualCollectionCount = actual.Count(); + // Check whether the element counts are different. - if (expected.Count != actual!.Count) + if (expectedCollectionCount != actualCollectionCount) { string userMessage = Assert.BuildUserMessage(message, parameters); string finalMessage = string.Format( CultureInfo.CurrentCulture, FrameworkMessages.ElementNumbersDontMatch, userMessage, - expected.Count, - actual.Count); + expectedCollectionCount, + actualCollectionCount); Assert.ThrowAssertFailed("CollectionAssert.AreEquivalent", finalMessage); } // If both collections are empty, they are equivalent. - if (expected.Count == 0) + if (!expected.Any()) { return; } // Search for a mismatched element. - if (FindMismatchedElement(expected, actual, out int expectedCount, out int actualCount, out object? mismatchedElement)) + if (FindMismatchedElement(expected, actual, comparer, out int expectedCount, out int actualCount, out object? mismatchedElement)) { string userMessage = Assert.BuildUserMessage(message, parameters); string finalMessage = string.Format( @@ -659,7 +762,7 @@ public static void AreEquivalent( /// public static void AreNotEquivalent( [NotNullIfNotNull(nameof(actual))] ICollection? expected, [NotNullIfNotNull(nameof(expected))] ICollection? actual) - => AreNotEquivalent(expected, actual, string.Empty, null); + => AreNotEquivalent(expected?.Cast(), actual?.Cast(), EqualityComparer.Default, string.Empty, null); /// /// Tests whether two collections contain the different elements and throws an @@ -687,7 +790,7 @@ public static void AreNotEquivalent( public static void AreNotEquivalent( [NotNullIfNotNull(nameof(actual))] ICollection? expected, [NotNullIfNotNull(nameof(expected))] ICollection? actual, string? message) - => AreNotEquivalent(expected, actual, message, null); + => AreNotEquivalent(expected?.Cast(), actual?.Cast(), EqualityComparer.Default, message, null); /// /// Tests whether two collections contain the different elements and throws an @@ -717,8 +820,109 @@ public static void AreNotEquivalent( /// public static void AreNotEquivalent( [NotNullIfNotNull(nameof(actual))] ICollection? expected, [NotNullIfNotNull(nameof(expected))] ICollection? actual, - string? message, params object?[]? parameters) + [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) + => AreNotEquivalent(expected?.Cast(), actual?.Cast(), comparer: EqualityComparer.Default, message, parameters); + + /// + /// Tests whether two collections contain the different elements and throws an + /// exception if the two collections contain identical elements without regard + /// to order. + /// + /// + /// The type of values to compare. + /// + /// + /// The first collection to compare. This contains the elements the test + /// expects to be different than the actual collection. + /// + /// + /// The second collection to compare. This is the collection produced by + /// the code under test. + /// + /// + /// The compare implementation to use when comparing elements of the collection. + /// + /// + /// and nullabilities don't match, + /// or if collections contain the same elements, including the same number of duplicate + /// occurrences of each element. + /// + public static void AreNotEquivalent( + [NotNullIfNotNull(nameof(actual))] IEnumerable? expected, [NotNullIfNotNull(nameof(expected))] IEnumerable? actual, [NotNull] IEqualityComparer? comparer) + => AreNotEquivalent(expected, actual, comparer, string.Empty, null); + + /// + /// Tests whether two collections contain the different elements and throws an + /// exception if the two collections contain identical elements without regard + /// to order. + /// + /// + /// The type of values to compare. + /// + /// + /// The first collection to compare. This contains the elements the test + /// expects to be different than the actual collection. + /// + /// + /// The second collection to compare. This is the collection produced by + /// the code under test. + /// + /// + /// The compare implementation to use when comparing elements of the collection. + /// + /// + /// The message to include in the exception when + /// contains the same elements as . The message + /// is shown in test results. + /// + /// + /// and nullabilities don't match, + /// or if collections contain the same elements, including the same number of duplicate + /// occurrences of each element. + /// + public static void AreNotEquivalent( + [NotNullIfNotNull(nameof(actual))] IEnumerable? expected, [NotNullIfNotNull(nameof(expected))] IEnumerable? actual, [NotNull] IEqualityComparer? comparer, + string? message) + => AreNotEquivalent(expected, actual, comparer, message, null); + + /// + /// Tests whether two collections contain the different elements and throws an + /// exception if the two collections contain identical elements without regard + /// to order. + /// + /// + /// The type of values to compare. + /// + /// + /// The first collection to compare. This contains the elements the test + /// expects to be different than the actual collection. + /// + /// + /// The second collection to compare. This is the collection produced by + /// the code under test. + /// + /// + /// The compare implementation to use when comparing elements of the collection. + /// + /// + /// The message to include in the exception when + /// contains the same elements as . The message + /// is shown in test results. + /// + /// + /// An array of parameters to use when formatting . + /// + /// + /// and nullabilities don't match, + /// or if collections contain the same elements, including the same number of duplicate + /// occurrences of each element. + /// + public static void AreNotEquivalent( + [NotNullIfNotNull(nameof(actual))] IEnumerable? expected, [NotNullIfNotNull(nameof(expected))] IEnumerable? actual, [NotNull] IEqualityComparer? comparer, + [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { + Assert.CheckParameterNotNull(comparer, "Assert.AreCollectionsEqual", "comparer", string.Empty); + // Check whether one is null while the other is not. if (expected == null != (actual == null)) { @@ -737,14 +941,17 @@ public static void AreNotEquivalent( Assert.ThrowAssertFailed("CollectionAssert.AreNotEquivalent", finalMessage); } + DebugEx.Assert(actual is not null, "actual is not null here"); + DebugEx.Assert(expected is not null, "expected is not null here"); + // Check whether the element counts are different. - if (expected!.Count != actual!.Count) + if (expected.Count() != actual.Count()) { return; } // If both collections are empty, they are equivalent. - if (expected.Count == 0) + if (!expected.Any()) { string userMessage = Assert.BuildUserMessage(message, parameters); string finalMessage = string.Format( @@ -755,7 +962,7 @@ public static void AreNotEquivalent( } // Search for a mismatched element. - if (!FindMismatchedElement(expected, actual, out _, out _, out _)) + if (!FindMismatchedElement(expected, actual, comparer, out _, out _, out _)) { string userMessage = Assert.BuildUserMessage(message, parameters); string finalMessage = string.Format( @@ -842,7 +1049,7 @@ public static void AllItemsAreInstancesOfType([NotNull] ICollection? collection, /// . /// public static void AllItemsAreInstancesOfType( - [NotNull] ICollection? collection, [NotNull] Type? expectedType, string? message, params object?[]? parameters) + [NotNull] ICollection? collection, [NotNull] Type? expectedType, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { Assert.CheckParameterNotNull(collection, "CollectionAssert.AllItemsAreInstancesOfType", "collection", string.Empty); Assert.CheckParameterNotNull(expectedType, "CollectionAssert.AllItemsAreInstancesOfType", "expectedType", string.Empty); @@ -945,7 +1152,7 @@ public static void AreEqual(ICollection? expected, ICollection? actual, string? /// Thrown if is not equal to /// . /// - public static void AreEqual(ICollection? expected, ICollection? actual, string? message, + public static void AreEqual(ICollection? expected, ICollection? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { string reason = string.Empty; @@ -1030,7 +1237,7 @@ public static void AreNotEqual(ICollection? notExpected, ICollection? actual, st /// /// Thrown if is equal to . /// - public static void AreNotEqual(ICollection? notExpected, ICollection? actual, string? message, + public static void AreNotEqual(ICollection? notExpected, ICollection? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { string reason = string.Empty; @@ -1123,7 +1330,7 @@ public static void AreEqual(ICollection? expected, ICollection? actual, [NotNull /// . /// public static void AreEqual(ICollection? expected, ICollection? actual, [NotNull] IComparer? comparer, - string? message, params object?[]? parameters) + [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { string reason = string.Empty; if (!AreCollectionsEqual(expected, actual, comparer, ref reason)) @@ -1215,7 +1422,7 @@ public static void AreNotEqual(ICollection? notExpected, ICollection? actual, [N /// Thrown if is equal to . /// public static void AreNotEqual(ICollection? notExpected, ICollection? actual, [NotNull] IComparer? comparer, - string? message, params object?[]? parameters) + [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { string reason = string.Empty; if (AreCollectionsEqual(notExpected, actual, comparer, ref reason)) @@ -1255,8 +1462,8 @@ internal static bool IsSubsetOfHelper(ICollection subset, ICollection superset) // $ CONSIDER: comparison, which should result in ~n*log(n) + m*log(m) + n. // Count the occurrences of each object in both collections. - Dictionary subsetElements = GetElementCounts(subset, out int subsetNulls); - Dictionary supersetElements = GetElementCounts(superset, out int supersetNulls); + Dictionary subsetElements = GetElementCounts(subset.Cast(), EqualityComparer.Default, out int subsetNulls); + Dictionary supersetElements = GetElementCounts(superset.Cast(), EqualityComparer.Default, out int supersetNulls); if (subsetNulls > supersetNulls) { @@ -1280,6 +1487,7 @@ internal static bool IsSubsetOfHelper(ICollection subset, ICollection superset) return true; } +#pragma warning disable CS8714 /// /// Constructs a dictionary containing the number of occurrences of each /// element in the specified collection. @@ -1294,14 +1502,14 @@ internal static bool IsSubsetOfHelper(ICollection subset, ICollection superset) /// A dictionary containing the number of occurrences of each element /// in the specified collection. /// - private static Dictionary GetElementCounts(ICollection collection, out int nullCount) + private static Dictionary GetElementCounts(IEnumerable collection, IEqualityComparer comparer, out int nullCount) { DebugEx.Assert(collection != null, "Collection is Null."); - Dictionary elementCounts = []; + var elementCounts = new Dictionary(comparer); nullCount = 0; - foreach (object? element in collection) + foreach (T? element in collection) { if (element == null) { @@ -1349,7 +1557,7 @@ private static Dictionary GetElementCounts(ICollection collection, /// /// true if a mismatched element was found; false otherwise. /// - private static bool FindMismatchedElement(ICollection expected, ICollection actual, out int expectedCount, + private static bool FindMismatchedElement(IEnumerable expected, IEnumerable actual, IEqualityComparer comparer, out int expectedCount, out int actualCount, out object? mismatchedElement) { // $ CONSIDER: The current algorithm counts the number of occurrences of each @@ -1359,8 +1567,8 @@ private static bool FindMismatchedElement(ICollection expected, ICollection actu // $ CONSIDER: comparison, which should result in ~n*log(n) + m*log(m) + n. // Count the occurrences of each object in the both collections - Dictionary expectedElements = GetElementCounts(expected, out int expectedNulls); - Dictionary actualElements = GetElementCounts(actual, out int actualNulls); + Dictionary expectedElements = GetElementCounts(expected, comparer, out int expectedNulls); + Dictionary actualElements = GetElementCounts(actual, comparer, out int actualNulls); if (actualNulls != expectedNulls) { @@ -1373,7 +1581,7 @@ private static bool FindMismatchedElement(ICollection expected, ICollection actu // Compare the counts of each object. Note that this comparison only needs // to be done one way since comparing the total count is a prerequisite to // calling this function. - foreach (object current in expectedElements.Keys) + foreach (T current in expectedElements.Keys) { expectedElements.TryGetValue(current, out expectedCount); actualElements.TryGetValue(current, out actualCount); @@ -1391,6 +1599,7 @@ private static bool FindMismatchedElement(ICollection expected, ICollection actu mismatchedElement = null; return false; } +#pragma warning restore CS8714 private static bool AreCollectionsEqual(ICollection? expected, ICollection? actual, [NotNull] IComparer? comparer, ref string reason) @@ -1443,6 +1652,5 @@ private class ObjectComparer : IComparer { int IComparer.Compare(object? x, object? y) => Equals(x, y) ? 0 : -1; } - #endregion } diff --git a/src/TestFramework/TestFramework/Assertions/StringAssert.cs b/src/TestFramework/TestFramework/Assertions/StringAssert.cs index 3fe58234c8..f39e1cd92b 100644 --- a/src/TestFramework/TestFramework/Assertions/StringAssert.cs +++ b/src/TestFramework/TestFramework/Assertions/StringAssert.cs @@ -12,7 +12,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// public sealed class StringAssert { - private static readonly object[] Empty = Array.Empty(); + private static readonly object[] Empty = []; #region Singleton constructor @@ -147,7 +147,7 @@ public static void Contains([NotNull] string? value, [NotNull] string? substring /// is null, or is null, /// or does not contain . /// - public static void Contains([NotNull] string? value, [NotNull] string? substring, string? message, + public static void Contains([NotNull] string? value, [NotNull] string? substring, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => Contains(value, substring, message, StringComparison.Ordinal, parameters); @@ -177,7 +177,7 @@ public static void Contains([NotNull] string? value, [NotNull] string? substring /// is null, or is null, /// or does not contain . /// - public static void Contains([NotNull] string? value, [NotNull] string? substring, string? message, + public static void Contains([NotNull] string? value, [NotNull] string? substring, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, StringComparison comparisonType, params object?[]? parameters) { Assert.CheckParameterNotNull(value, "StringAssert.Contains", "value", string.Empty); @@ -276,7 +276,7 @@ public static void StartsWith([NotNull] string? value, [NotNull] string? substri /// is null, or is null, /// or does not start with . /// - public static void StartsWith([NotNull] string? value, [NotNull] string? substring, string? message, + public static void StartsWith([NotNull] string? value, [NotNull] string? substring, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => StartsWith(value, substring, message, StringComparison.Ordinal, parameters); @@ -333,7 +333,7 @@ public static void StartsWith([NotNull] string? value, [NotNull] string? substri /// is null, or is null, /// or does not start with . /// - public static void StartsWith([NotNull] string? value, [NotNull] string? substring, string? message, + public static void StartsWith([NotNull] string? value, [NotNull] string? substring, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, StringComparison comparisonType, params object?[]? parameters) { Assert.CheckParameterNotNull(value, "StringAssert.StartsWith", "value", string.Empty); @@ -432,7 +432,7 @@ public static void EndsWith([NotNull] string? value, [NotNull] string? substring /// is null, or is null, /// or does not end with . /// - public static void EndsWith([NotNull] string? value, [NotNull] string? substring, string? message, + public static void EndsWith([NotNull] string? value, [NotNull] string? substring, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) => EndsWith(value, substring, message, StringComparison.Ordinal, parameters); @@ -489,7 +489,7 @@ public static void EndsWith([NotNull] string? value, [NotNull] string? substring /// is null, or is null, /// or does not end with . /// - public static void EndsWith([NotNull] string? value, [NotNull] string? substring, string? message, + public static void EndsWith([NotNull] string? value, [NotNull] string? substring, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, StringComparison comparisonType, params object?[]? parameters) { Assert.CheckParameterNotNull(value, "StringAssert.EndsWith", "value", string.Empty); @@ -570,7 +570,7 @@ public static void Matches([NotNull] string? value, [NotNull] Regex? pattern, st /// is null, or is null, /// or does not match . /// - public static void Matches([NotNull] string? value, [NotNull] Regex? pattern, string? message, params object?[]? parameters) + public static void Matches([NotNull] string? value, [NotNull] Regex? pattern, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { Assert.CheckParameterNotNull(value, "StringAssert.Matches", "value", string.Empty); Assert.CheckParameterNotNull(pattern, "StringAssert.Matches", "pattern", string.Empty); @@ -647,7 +647,7 @@ public static void DoesNotMatch([NotNull] string? value, [NotNull] Regex? patter /// is null, or is null, /// or matches . /// - public static void DoesNotMatch([NotNull] string? value, [NotNull] Regex? pattern, string? message, params object?[]? parameters) + public static void DoesNotMatch([NotNull] string? value, [NotNull] Regex? pattern, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { Assert.CheckParameterNotNull(value, "StringAssert.DoesNotMatch", "value", string.Empty); Assert.CheckParameterNotNull(pattern, "StringAssert.DoesNotMatch", "pattern", string.Empty); diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/DataRowAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/DataRowAttribute.cs index 2956b3559a..7c5e3f367f 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/DataRowAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/DataRowAttribute.cs @@ -1,6 +1,7 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Collections; using System.Globalization; using System.Reflection; @@ -35,7 +36,7 @@ public DataRowAttribute(object? data) /// /// The string array data. public DataRowAttribute(string?[]? stringArrayData) - : this(new object?[] { stringArrayData }) + : this([stringArrayData]) { } @@ -48,6 +49,8 @@ public DataRowAttribute(params object?[]? data) Data = data ?? [null]; } + protected internal static TestIdGenerationStrategy TestIdGenerationStrategy { get; internal set; } + /// /// Gets data for calling test method. /// @@ -59,7 +62,7 @@ public DataRowAttribute(params object?[]? data) public string? DisplayName { get; set; } /// - public IEnumerable GetData(MethodInfo methodInfo) => new[] { Data }; + public IEnumerable GetData(MethodInfo methodInfo) => [Data]; /// public virtual string? GetDisplayName(MethodInfo methodInfo, object?[]? data) @@ -80,10 +83,41 @@ public DataRowAttribute(params object?[]? data) // so that null do appear as "null". If you remove the call, and do string.Join(",", new object[] { null, "a" }), // you will get empty string while with the call you will get "null,a". IEnumerable displayData = parameters.Length == 1 && parameters[0].ParameterType == typeof(object[]) - ? new object[] { data.AsEnumerable() } + ? [data.AsEnumerable()] : data.AsEnumerable(); return string.Format(CultureInfo.CurrentCulture, FrameworkMessages.DataDrivenResultDisplayName, methodInfo.Name, - string.Join(",", displayData)); + string.Join(",", displayData.Select(GetObjectString))); + } + + /// + /// Recursively resolve collections of objects to a proper string representation. + /// + private static string? GetObjectString(object? obj) + { + if (TestIdGenerationStrategy != TestIdGenerationStrategy.FullyQualified) + { + return obj?.ToString(); + } + + if (obj == null) + { + return "null"; + } + + if (!obj.GetType().IsArray) + { + return obj switch + { + string s => $"\"{s}\"", + char c => $"'{c}'", + _ => obj.ToString(), + }; + } + + // We need to box the object here so that we can support value types + IEnumerable boxedObjectEnumerable = ((IEnumerable)obj).Cast(); + IEnumerable elementStrings = boxedObjectEnumerable.Select(GetObjectString); + return $"[{string.Join(",", elementStrings)}]"; } } diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/DataSourceAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/DataSourceAttribute.cs index fd7d208448..0644aa3eae 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/DataSourceAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/DataSourceAttribute.cs @@ -13,7 +13,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// [DataSource("dataSourceNameFromConfigFile")]. /// [SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification = "Compat")] -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method)] public sealed class DataSourceAttribute : Attribute { // DefaultProviderName needs not to be constant so that clients do not need diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/DataTestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/DataTestMethodAttribute.cs index a1603092e0..70d7372b43 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/DataTestMethodAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/DataTestMethodAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Attribute for data driven test where data can be specified in-line. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method)] public class DataTestMethodAttribute : TestMethodAttribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataAttribute.cs index bebfe0e03d..7624071013 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataAttribute.cs @@ -97,8 +97,7 @@ public IEnumerable GetData(MethodInfo methodInfo) case DynamicDataSourceType.Property: PropertyInfo property = _dynamicDataDeclaringType.GetTypeInfo().GetDeclaredProperty(_dynamicDataSourceName) ?? throw new ArgumentNullException($"{DynamicDataSourceType.Property} {_dynamicDataSourceName}"); - if (property.GetGetMethod(true) is not { } getMethod - || !getMethod.IsStatic) + if (property.GetGetMethod(true) is not { IsStatic: true }) { throw new NotSupportedException( string.Format( diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceDiscoveryAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceDiscoveryAttribute.cs index 279947b10e..d12c870563 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceDiscoveryAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceDiscoveryAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Specifies how to discover tests. /// -[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Assembly)] public class TestDataSourceDiscoveryAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/TestIdGenerationStrategyAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/TestIdGenerationStrategyAttribute.cs index 6d0d1e6884..3b5720e051 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/TestIdGenerationStrategyAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/TestIdGenerationStrategyAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Specifies how to generate test ID. /// -[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Assembly)] public class TestIdGenerationStrategyAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/AssemblyCleanupAttribute.cs b/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/AssemblyCleanupAttribute.cs index 735bcae594..5d37d858f7 100644 --- a/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/AssemblyCleanupAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/AssemblyCleanupAttribute.cs @@ -6,7 +6,5 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// The assembly cleanup attribute. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] -public sealed class AssemblyCleanupAttribute : Attribute -{ -} +[AttributeUsage(AttributeTargets.Method)] +public sealed class AssemblyCleanupAttribute : Attribute; diff --git a/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/ClassCleanupAttribute.cs b/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/ClassCleanupAttribute.cs index 55ef70e430..6553361f0c 100644 --- a/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/ClassCleanupAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/ClassCleanupAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// The class cleanup attribute. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method)] public sealed class ClassCleanupAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/ClassCleanupExecutionAttribute.cs b/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/ClassCleanupExecutionAttribute.cs index 0285a662ad..6b42b673ba 100644 --- a/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/ClassCleanupExecutionAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/ClassCleanupExecutionAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Specification for when to run class cleanup methods. /// -[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = true)] +[AttributeUsage(AttributeTargets.Assembly)] public class ClassCleanupExecutionAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/TestCleanupAttribute.cs b/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/TestCleanupAttribute.cs index c3657d9fe7..b3ccf43937 100644 --- a/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/TestCleanupAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/Lifecycle/Cleanup/TestCleanupAttribute.cs @@ -6,7 +6,5 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// The test cleanup attribute marks methods that are executed after every test marked with a . /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] -public sealed class TestCleanupAttribute : Attribute -{ -} +[AttributeUsage(AttributeTargets.Method)] +public sealed class TestCleanupAttribute : Attribute; diff --git a/src/TestFramework/TestFramework/Attributes/Lifecycle/DoNotParallelizeAttribute.cs b/src/TestFramework/TestFramework/Attributes/Lifecycle/DoNotParallelizeAttribute.cs index 41b79a7b74..3191c59e1b 100644 --- a/src/TestFramework/TestFramework/Attributes/Lifecycle/DoNotParallelizeAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/Lifecycle/DoNotParallelizeAttribute.cs @@ -6,7 +6,5 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Specification to disable parallelization. /// -[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] -public class DoNotParallelizeAttribute : Attribute -{ -} +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] +public class DoNotParallelizeAttribute : Attribute; diff --git a/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/AssemblyInitializeAttribute.cs b/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/AssemblyInitializeAttribute.cs index e4ac4ac08c..c45d0124e5 100644 --- a/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/AssemblyInitializeAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/AssemblyInitializeAttribute.cs @@ -6,7 +6,5 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// The assembly initialize attribute. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] -public sealed class AssemblyInitializeAttribute : Attribute -{ -} +[AttributeUsage(AttributeTargets.Method)] +public sealed class AssemblyInitializeAttribute : Attribute; diff --git a/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/ClassInitializeAttribute.cs b/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/ClassInitializeAttribute.cs index 705ba765a4..d55b980d95 100644 --- a/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/ClassInitializeAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/ClassInitializeAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// The class initialize attribute. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method)] public sealed class ClassInitializeAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/DiscoverInternalsAttribute.cs b/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/DiscoverInternalsAttribute.cs index f79b8dc1ea..94ad9c1aac 100644 --- a/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/DiscoverInternalsAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/DiscoverInternalsAttribute.cs @@ -9,7 +9,5 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// internal in addition to test classes and test methods which are declared public. When this attribute is not /// present in a test assembly the tests in such classes will not be discovered. /// -[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] -public class DiscoverInternalsAttribute : Attribute -{ -} +[AttributeUsage(AttributeTargets.Assembly)] +public class DiscoverInternalsAttribute : Attribute; diff --git a/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/TestInitializeAttribute.cs b/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/TestInitializeAttribute.cs index 72df6d73aa..3314f63627 100644 --- a/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/TestInitializeAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/Lifecycle/Initialization/TestInitializeAttribute.cs @@ -6,7 +6,5 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// The test initialize attribute. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] -public sealed class TestInitializeAttribute : Attribute -{ -} +[AttributeUsage(AttributeTargets.Method)] +public sealed class TestInitializeAttribute : Attribute; diff --git a/src/TestFramework/TestFramework/Attributes/Lifecycle/ParallelizeAttribute.cs b/src/TestFramework/TestFramework/Attributes/Lifecycle/ParallelizeAttribute.cs index 1c2ff3e6e1..732fe4993a 100644 --- a/src/TestFramework/TestFramework/Attributes/Lifecycle/ParallelizeAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/Lifecycle/ParallelizeAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Specification for parallelization level for a test run. /// -[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Assembly)] public class ParallelizeAttribute : Attribute { private const int DefaultParallelWorkers = 0; diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/CssIterationAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/CssIterationAttribute.cs index 0626d268ab..45c1e08f49 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/CssIterationAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/CssIterationAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// CSS Iteration URI. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method)] public sealed class CssIterationAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/CssProjectStructureAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/CssProjectStructureAttribute.cs index ac7aaabf6d..b75b5b7e43 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/CssProjectStructureAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/CssProjectStructureAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// CSS Project Structure URI. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method)] public sealed class CssProjectStructureAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/DescriptionAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/DescriptionAttribute.cs index 23c0050a6d..3a153b21f8 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/DescriptionAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/DescriptionAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Description of the test. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method)] public sealed class DescriptionAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionAttribute.cs index ca877ed410..f57595fe8c 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Globalization; @@ -9,7 +9,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Attribute that specifies to expect an exception of the specified type. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] +[AttributeUsage(AttributeTargets.Method)] public sealed class ExpectedExceptionAttribute : ExpectedExceptionBaseAttribute { #region Constructors @@ -39,7 +39,7 @@ public ExpectedExceptionAttribute(Type exceptionType, string noExceptionMessage) throw new ArgumentNullException(nameof(exceptionType)); } - if (!typeof(Exception).GetTypeInfo().IsAssignableFrom(exceptionType.GetTypeInfo())) + if (!typeof(Exception).IsAssignableFrom(exceptionType.GetTypeInfo())) { throw new ArgumentException( FrameworkMessages.UTF_ExpectedExceptionTypeMustDeriveFromException, @@ -56,21 +56,13 @@ public ExpectedExceptionAttribute(Type exceptionType, string noExceptionMessage) /// /// Gets a value indicating the Type of the expected exception. /// - public Type ExceptionType - { - get; - private set; - } + public Type ExceptionType { get; } /// /// Gets or sets a value indicating whether to allow types derived from the type of the expected exception to /// qualify as expected. /// - public bool AllowDerivedTypes - { - get; - set; - } + public bool AllowDerivedTypes { get; set; } /// /// Gets the message to include in the test result if the test fails due to not throwing an exception. @@ -96,7 +88,7 @@ protected internal override void Verify(Exception exception) Type thrownExceptionType = exception.GetType(); if (AllowDerivedTypes) { - if (!ExceptionType.GetTypeInfo().IsAssignableFrom(thrownExceptionType.GetTypeInfo())) + if (!ExceptionType.IsAssignableFrom(thrownExceptionType.GetTypeInfo())) { // If the exception is an AssertFailedException or an AssertInconclusiveException, then re-throw it to // preserve the test outcome and error message diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionBaseAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionBaseAttribute.cs index 9dd41158fe..a5252aad6c 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionBaseAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionBaseAttribute.cs @@ -9,7 +9,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Base class for attributes that specify to expect an exception from a unit test. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] +[AttributeUsage(AttributeTargets.Method)] public abstract class ExpectedExceptionBaseAttribute : Attribute { /// @@ -60,7 +60,7 @@ protected internal virtual string NoExceptionMessage /// /// Gets the message to include in the test result if the test fails due to not throwing an exception. /// - protected string SpecifiedNoExceptionMessage { get; private set; } + protected string SpecifiedNoExceptionMessage { get; } /// /// Gets the default no-exception message. diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/IgnoreAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/IgnoreAttribute.cs index d044f802a1..b55e34fe00 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/IgnoreAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/IgnoreAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// The ignore attribute. /// -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public sealed class IgnoreAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/OwnerAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/OwnerAttribute.cs index b69741bd15..91e1a76981 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/OwnerAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/OwnerAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Test Owner. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method)] public sealed class OwnerAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/PriorityAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/PriorityAttribute.cs index be48e88158..4c59ec1b9a 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/PriorityAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/PriorityAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Priority attribute; used to specify the priority of a unit test. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method)] public sealed class PriorityAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TestCategoryAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TestCategoryAttribute.cs index 34ace0cd23..058130734b 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TestCategoryAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TestCategoryAttribute.cs @@ -20,10 +20,10 @@ public sealed class TestCategoryAttribute : TestCategoryBaseAttribute /// public TestCategoryAttribute(string testCategory) { - List categories = new(1) - { - testCategory, - }; + List categories = + [ + testCategory + ]; TestCategories = categories; } diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TestClassAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TestClassAttribute.cs index 53e710d2b2..77d80855cd 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TestClassAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TestClassAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// The test class attribute. /// -[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Class)] public class TestClassAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TestMethodAttribute.cs index fc0ac5eea1..f81cbaaec7 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TestMethodAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TestMethodAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// The test method attribute. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method)] public class TestMethodAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TimeoutAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TimeoutAttribute.cs index 68299542b8..945ba71cb0 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TimeoutAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TimeoutAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Timeout attribute; used to specify the timeout of a unit test. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method)] public sealed class TimeoutAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Internal/UtfHelper.cs b/src/TestFramework/TestFramework/Internal/UtfHelper.cs index f67560fdcc..a9ebbc8932 100644 --- a/src/TestFramework/TestFramework/Internal/UtfHelper.cs +++ b/src/TestFramework/TestFramework/Internal/UtfHelper.cs @@ -32,6 +32,7 @@ internal static string GetExceptionMsg(Exception ex) // Get the exception message. Need to check for errors because the Message property // may have been overridden by the exception type in user code. string msg; + Type type = curException.GetType(); try { msg = curException.Message; @@ -41,15 +42,26 @@ internal static string GetExceptionMsg(Exception ex) msg = string.Format( CultureInfo.CurrentCulture, FrameworkMessages.UTF_FailedToGetExceptionMessage, - curException.GetType()); + type); } - result.AppendFormat( + if (first) + { + result.AppendFormat( CultureInfo.CurrentCulture, - "{0}{1}: {2}", - first ? string.Empty : " ---> ", - curException.GetType(), + "{0}: {1}", + type, msg); + } + else + { + result.AppendFormat( + CultureInfo.CurrentCulture, + " ---> {0}: {1}", + type, + msg); + } + first = false; } diff --git a/src/TestFramework/TestFramework/Logger.cs b/src/TestFramework/TestFramework/Logger.cs index 5f0b9a7205..858d5d90a5 100644 --- a/src/TestFramework/TestFramework/Logger.cs +++ b/src/TestFramework/TestFramework/Logger.cs @@ -51,12 +51,13 @@ public static void LogMessage(string format, params object?[] args) ? format : string.Format(CultureInfo.InvariantCulture, format, args); + object?[] parameters = [message]; // Making sure all event handlers are called in sync on same thread. - foreach (Delegate? invoker in OnLogMessage.GetInvocationList()) + foreach (Delegate invoker in OnLogMessage.GetInvocationList()) { try { - invoker!.GetMethodInfo()!.Invoke(invoker.Target, new[] { message }); + invoker.GetMethodInfo()!.Invoke(invoker.Target, parameters); } catch (Exception) { diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt index 82e656b659..3b6ba9164d 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt @@ -306,6 +306,9 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(obje static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, System.Type? expectedType, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, System.Type? expectedType, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, string? message, params object?[]? parameters) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, System.Type? wrongType) -> void diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt index aba4894786..93ea1cc614 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt @@ -1,4 +1,8 @@ #nullable enable -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance, string? message) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance, string? message, params object?[]? parameters) -> void \ No newline at end of file +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message, params object?[]? parameters) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message, params object?[]? parameters) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.TestIdGenerationStrategy.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestIdGenerationStrategy diff --git a/src/TestFramework/TestFramework/TestFramework.csproj b/src/TestFramework/TestFramework/TestFramework.csproj index f5b195e0d9..814ffc7290 100644 --- a/src/TestFramework/TestFramework/TestFramework.csproj +++ b/src/TestFramework/TestFramework/TestFramework.csproj @@ -19,7 +19,10 @@ - + + + + diff --git a/test/Directory.Build.props b/test/Directory.Build.props new file mode 100644 index 0000000000..d23ac85189 --- /dev/null +++ b/test/Directory.Build.props @@ -0,0 +1,22 @@ + + + + + + + + + false + true + $(DefineConstants);NATIVE_AOT + $(DefineConstants);ENABLE_CODECOVERAGE + + + + + false + true + false + + + diff --git a/test/Directory.Build.targets b/test/Directory.Build.targets new file mode 100644 index 0000000000..5ee5e36468 --- /dev/null +++ b/test/Directory.Build.targets @@ -0,0 +1,55 @@ + + + + + + + enable + Exe + + false + $(PlatformTarget) + x64 + $(MSBuildProjectName)_$(TargetFramework)_$(Configuration)_$(Architecture) + + + $(TestRunnerAdditionalArguments) --diagnostic --diagnostic-output-directory $(RepoRoot)artifacts/log/$(Configuration) --diagnostic-output-fileprefix $(ModuleName) --diagnostic-verbosity trace + $(TestRunnerAdditionalArguments) --crashdump + $(TestRunnerAdditionalArguments) --hangdump --hangdump-timeout 15m + $(TestRunnerAdditionalArguments) --coverage --coverage-settings $(RepoRoot)test/coverage.config --coverage-output $(ModuleName).coverage + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolutionTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolutionTests.cs index 422525cba2..2147dff209 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolutionTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolutionTests.cs @@ -57,8 +57,8 @@ public async Task AssemblyResolution_WhenSpecified_TestSucceeds() public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : IAsyncInitializable, IDisposable { public const string ProjectName = "AssemblyResolution.Main"; - private const string TargetFramework = "net6.0"; private const string TestProjectName = "AssemblyResolution.Test"; + private static readonly string TargetFramework = TargetFrameworks.NetCurrent.Arguments; private readonly TempDirectory _testAssetDirectory = new(); @@ -136,7 +136,7 @@ public class Class1 using Microsoft.VisualStudio.TestTools.UnitTesting; namespace AssemblyResolution.Test; - + [TestClass] public class UnitTest1 { diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolverTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolverTests.cs index 01fab28cc0..5e0995dc69 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolverTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolverTests.cs @@ -10,8 +10,8 @@ namespace MSTest.Acceptance.IntegrationTests; [TestGroup] public class AssemblyResolverTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; private const string AssetName = "AssemblyResolverCrash"; + private readonly TestAssetFixture _testAssetFixture; // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). public AssemblyResolverTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, @@ -31,7 +31,7 @@ public async Task RunningTests_DoesNotHitResourceRecursionIssueAndDoesNotCrashTh var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, TargetFrameworks.NetFramework[0].Arguments); - var testHostResult = await testHost.ExecuteAsync(); + TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertExitCodeIs(ExitCodes.Success); } @@ -63,7 +63,6 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test - diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DotnetTestCliTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DotnetTestCliTests.cs similarity index 91% rename from test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DotnetTestCliTests.cs rename to test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DotnetTestCliTests.cs index fb59fee176..b9f9f7b652 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DotnetTestCliTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DotnetTestCliTests.cs @@ -28,10 +28,8 @@ public async Task DotnetTest_Should_Execute_Tests(string tfm, BuildConfiguration .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) .PatchCodeWithReplace("$EnableMSTestRunner$", string.Empty) .PatchCodeWithReplace("$OutputType$", string.Empty) - .PatchCodeWithReplace("$Extra$", string.Empty), - addPublicFeeds: true); + .PatchCodeWithReplace("$Extra$", string.Empty)); - string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -m:1 -nodeReuse:false {generator.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); // There is whitespace difference in output in parent and public repo that depends on the version of the dotnet SDK used. diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DynamicDataTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DynamicDataTests.cs deleted file mode 100644 index ecf3c74fbf..0000000000 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DynamicDataTests.cs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Testing.Platform.Acceptance.IntegrationTests; -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - -namespace MSTest.Acceptance.IntegrationTests; - -[TestGroup] -public class DynamicDataTests : AcceptanceTestBase -{ - private readonly TestAssetFixture _testAssetFixture; - private const string AssetName = "DynamicData"; - - // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). - public DynamicDataTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, - AcceptanceFixture globalFixture) - : base(testExecutionContext) - { - _testAssetFixture = testAssetFixture; - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusive_Passes(string currentTfm) - { - string runSettings = $""" - - - - - - true - - -"""; - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); - - string runSettingsFilePath = Path.Combine(testHost.DirectoryName, $"{Guid.NewGuid():N}.runsettings"); - File.WriteAllText(runSettingsFilePath, runSettings); - - var testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}"); - - testHostResult.AssertExitCodeIs(ExitCodes.Success); - - testHostResult.AssertOutputContains("skipped Test"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusiveToFalse_Fails(string currentTfm) - { - string runSettings = $""" - - - - - - false - - -"""; - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); - - string runSettingsFilePath = Path.Combine(testHost.DirectoryName, $"{Guid.NewGuid():N}.runsettings"); - File.WriteAllText(runSettingsFilePath, runSettings); - - var testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}"); - - testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); - - testHostResult.AssertOutputContains("failed Test"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task SendingEmptyDataToDynamicDataTest_WithoutSettingConsiderEmptyDataSourceAsInconclusive_Fails(string currentTfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); - - var testHostResult = await testHost.ExecuteAsync(); - - testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); - - testHostResult.AssertOutputContains("failed Test"); - } - - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) - { - public string TargetAssetPath => GetAssetPath(AssetName); - - public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() - { - yield return (AssetName, AssetName, - SourceCode - .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); - } - - private const string SourceCode = """ -#file DynamicData.csproj - - - - Exe - true - $TargetFrameworks$ - - - - - - - - - - -#file UnitTest1.cs - -using System; -using System.Collections.Generic; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public class TestClass -{ - [TestMethod] - [DynamicData(nameof(AdditionalData))] - [DynamicData(nameof(AdditionalData2))] - public void Test() - { - } - - public static IEnumerable AdditionalData => Array.Empty(); - - public static IEnumerable AdditionalData2 - { - get - { - yield return 2; - } - } -} -"""; - } -} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/HelpInfoTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/HelpInfoTests.cs new file mode 100644 index 0000000000..678194bb15 --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/HelpInfoTests.cs @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests; +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +namespace MSTest.Acceptance.IntegrationTests; + +[TestGroup] +public class HelpInfoTests : AcceptanceTestBase +{ + private const string AssetName = "HelpInfo"; + private readonly TestAssetFixture _testAssetFixture; + + // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). + public HelpInfoTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, + AcceptanceFixture globalFixture) + : base(testExecutionContext) + { + _testAssetFixture = testAssetFixture; + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task Help_WhenMSTestExtensionRegistered_OutputHelpContentOfRegisteredExtension(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + + string wildcardMatchPattern = $""" +MSTest v{MSTestVersion} (UTC *) [* - *] +Usage {AssetName}* [option providers] [extension option providers] +Execute a .NET Test Application. +Options: + --diagnostic Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag + --diagnostic-filelogger-synchronouswrite Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). Note that this is slowing down the test execution. + --diagnostic-output-directory Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory. + --diagnostic-output-fileprefix Prefix for the log file name that will replace '[log]_.' + --diagnostic-verbosity Define the level of the verbosity for the --diagnostic. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical' + --exit-on-process-exit Exit the test process if dependent process exits. PID must be provided. + --help Show the command line help. + --ignore-exit-code Do not report non successful exit value for specific exit codes (e.g. '--ignore-exit-code 8;9' ignore exit code 8 and 9 and will return 0 in these case) + --info Display .NET test application information. + --list-tests List available tests. + --minimum-expected-tests Specifies the minimum number of tests that are expected to run. + --results-directory The directory where the test results are going to be placed. If the specified directory doesn't exist, it's created. The default is TestResults in the directory that contains the test application. +Extension options: + --filter Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + --settings The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + --test-parameter Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters +"""; + + testHostResult.AssertOutputMatches(wildcardMatchPattern); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task Info_WhenMSTestExtensionRegistered_OutputInfoContentOfRegisteredExtension(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--info"); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + + string output = $""" + MSTestExtension + Name: MSTest + Version: {MSTestVersion} + Description: MSTest Framework for Microsoft Testing Platform + Options: + --settings + Arity: 1 + Hidden: False + Description: The path, relative or absolute, to the .runsettings file. For more information and examples on how to configure test run, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file + --filter + Arity: 1 + Hidden: False + Description: Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + --test-parameter + Arity: 1..N + Hidden: False + Description: Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters +Registered tools: + There are no registered tools. +"""; + + testHostResult.AssertOutputContains(output); + } + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + public string TargetAssetPath => GetAssetPath(AssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (AssetName, AssetName, + SourceCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceCode = """ +#file HelpInfo.csproj + + + + Exe + true + $TargetFrameworks$ + + + + + + + + + +#file UnitTest1.cs + +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class TestClass +{ + [TestMethod] + public void Test() {} +} +"""; + } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/IgnoreTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/IgnoreTests.cs new file mode 100644 index 0000000000..d8943e2534 --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/IgnoreTests.cs @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests; +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; + +namespace MSTest.Acceptance.IntegrationTests; + +[TestGroup] +public sealed class IgnoreTests : AcceptanceTestBase +{ + private readonly TestAssetFixture _testAssetFixture; + + public IgnoreTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) + : base(testExecutionContext) + { + _testAssetFixture = testAssetFixture; + } + + public async Task ClassCleanup_Inheritance_WhenClassIsSkipped() + { + var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings"); + + // Assert + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContains("Passed! - Failed: 0, Passed: 1, Skipped: 1, Total: 2"); + + testHostResult.AssertOutputContains("SubClass.Method"); + testHostResult.AssertOutputContains("SubClass.ClassCleanup"); + } + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + public const string ProjectName = "TestIgnore"; + + public string ProjectPath => GetAssetPath(ProjectName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (ProjectName, ProjectName, + SourceCode + .PatchTargetFrameworks(TargetFrameworks.NetCurrent) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceCode = """ +#file TestIgnore.csproj + + + + Exe + true + $TargetFrameworks$ + + + + + + + + + + PreserveNewest + + + + + +#file my.runsettings + + + false + + + +#file UnitTest1.cs +using System; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[Ignore] +[TestClass] +public class UnitTest1 +{ + [ClassCleanup] + public static void ClassCleanup() + => throw new InvalidOperationException("ClassCleanup should not be called"); + + [TestMethod] + public void Method() + => throw new InvalidOperationException("Test method should not be called"); +} + +[TestClass] +public class BaseClass +{ + [ClassCleanup] + public static void BaseClassCleanup() + => Console.WriteLine("BaseClass.ClassCleanup"); +} + +[Ignore] +[TestClass] +public class IntermediateClass : BaseClass +{ + [ClassCleanup] + public static void IntermediateClassCleanup() + => throw new InvalidOperationException("IntermediateClass.ClassCleanup should not be called"); +} + +[TestClass] +public class SubClass : IntermediateClass +{ + [ClassCleanup] + public static void SubClassCleanup() + => Console.WriteLine("SubClass.ClassCleanup"); + + [TestMethod] + public void Method() + => Console.WriteLine("SubClass.Method"); +} +"""; + } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InfoTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InfoTests.cs deleted file mode 100644 index c60080dc7f..0000000000 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InfoTests.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Testing.Platform.Acceptance.IntegrationTests; -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - -namespace MSTest.Acceptance.IntegrationTests; - -[TestGroup] -public class InfoTests : AcceptanceTestBase -{ - private readonly TestAssetFixture _testAssetFixture; - private const string AssetName = "Info"; - - // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). - public InfoTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, - AcceptanceFixture globalFixture) - : base(testExecutionContext) - { - _testAssetFixture = testAssetFixture; - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task UsingInfoArgument_OutputCorrectVersionForMSTest(string currentTfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); - - TestHostResult testHostResult = await testHost.ExecuteAsync("--info"); - - testHostResult.AssertExitCodeIs(ExitCodes.Success); - testHostResult.AssertOutputContains($""" - Name: MSTest - Version: {MSTestVersion} -"""); - } - - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) - { - public string TargetAssetPath => GetAssetPath(AssetName); - - public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() - { - yield return (AssetName, AssetName, - SourceCode - .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); - } - - private const string SourceCode = """ -#file Info.csproj - - - - Exe - true - $TargetFrameworks$ - - - - - - - - - - -#file UnitTest1.cs - -using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public class TestClass -{ - [TestMethod] - public void Test() {} -} -"""; - } -} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InitializeAndCleanupTimeoutTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InitializeAndCleanupTimeoutTests.cs index bf0ee0d95e..bfd29f8f4d 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InitializeAndCleanupTimeoutTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InitializeAndCleanupTimeoutTests.cs @@ -178,7 +178,7 @@ private async Task RunAndAssertTestTimedOutAsync(string rootFolder, string asset { var testHost = TestHost.LocateFrom(rootFolder, assetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { { envVarPrefix + InfoByKind[entryKind].EnvVarSuffix, "1" } }); - testHostResult.AssertOutputContains($"{InfoByKind[entryKind].Prefix} method '{InfoByKind[entryKind].MethodFullName}' timed out"); + testHostResult.AssertOutputContains($"{InfoByKind[entryKind].Prefix} method '{InfoByKind[entryKind].MethodFullName}' timed out after 1000ms"); } private async Task RunAndAssertWithRunSettingsAsync(string tfm, int timeoutValue, bool assertAttributePrecedence, string entryKind) @@ -195,6 +195,9 @@ private async Task RunAndAssertWithRunSettingsAsync(string tfm, int timeoutValue """; + // if assertAttributePrecedence is set we will use CodeWithOneSecTimeoutAssetPath + timeoutValue = assertAttributePrecedence ? 1000 : timeoutValue; + TestHost testHost = assertAttributePrecedence ? TestHost.LocateFrom(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm) : TestHost.LocateFrom(_testAssetFixture.CodeWithNoTimeoutAssetPath, TestAssetFixture.CodeWithNoTimeout, tfm); @@ -210,7 +213,7 @@ private async Task RunAndAssertWithRunSettingsAsync(string tfm, int timeoutValue Assert.IsTrue(stopwatch.Elapsed.TotalSeconds < 25); } - testHostResult.AssertOutputContains($"{InfoByKind[entryKind].Prefix} method '{InfoByKind[entryKind].MethodFullName}' timed out"); + testHostResult.AssertOutputContains($"{InfoByKind[entryKind].Prefix} method '{InfoByKind[entryKind].MethodFullName}' timed out after {timeoutValue}ms"); } [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] @@ -234,7 +237,6 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test .PatchCodeWithReplace("$ProjectName$", CodeWithNoTimeout) .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); yield return (CodeWithOneSecTimeout, CodeWithOneSecTimeout, @@ -243,7 +245,6 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test .PatchCodeWithReplace("$ProjectName$", CodeWithOneSecTimeout) .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); yield return (CodeWithSixtySecTimeout, CodeWithSixtySecTimeout, @@ -252,7 +253,6 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test .PatchCodeWithReplace("$ProjectName$", CodeWithSixtySecTimeout) .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); } @@ -269,7 +269,6 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test - diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildMSTestRunnerTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSBuildRunnerTests.cs similarity index 95% rename from test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildMSTestRunnerTests.cs rename to test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSBuildRunnerTests.cs index 097c8855fe..3068137fbe 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildMSTestRunnerTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSBuildRunnerTests.cs @@ -6,13 +6,13 @@ namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; [TestGroup] -public class MSBuildMSTestRunnerTests : AcceptanceTestBase +public class MSBuildRunnerTests : AcceptanceTestBase { private const string AssetName = "MSTestProject"; private const string DotnetTestVerb = "test"; private readonly AcceptanceFixture _acceptanceFixture; - public MSBuildMSTestRunnerTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) + public MSBuildRunnerTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) : base(testExecutionContext) { _acceptanceFixture = acceptanceFixture; @@ -51,8 +51,7 @@ public async Task MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests( true false """ : - string.Empty), - addPublicFeeds: true); + string.Empty)); string projectContent = File.ReadAllText(Directory.GetFiles(generator.TargetAssetPath, "MSTestProject.csproj", SearchOption.AllDirectories).Single()); string testSourceContent = File.ReadAllText(Directory.GetFiles(generator.TargetAssetPath, "UnitTest1.cs", SearchOption.AllDirectories).Single()); @@ -65,7 +64,7 @@ public async Task MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests( string nugetFile = solution.AddOrUpdateFileContent("Nuget.config", nugetConfigContent); for (int i = 0; i < 3; i++) { - CSharpProject project = solution.CreateCSharpProject($"TestProject{i}", isMultiTfm ? singleTfmOrMultiTfm.Split(';') : new[] { singleTfmOrMultiTfm }); + CSharpProject project = solution.CreateCSharpProject($"TestProject{i}", isMultiTfm ? singleTfmOrMultiTfm.Split(';') : [singleTfmOrMultiTfm]); File.WriteAllText(project.ProjectFile, projectContent); project.AddOrUpdateFileContent("UnitTest1.cs", testSourceContent); } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.Acceptance.IntegrationTests.csproj b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.Acceptance.IntegrationTests.csproj index 6c1ac04cf6..9f9264ffc9 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.Acceptance.IntegrationTests.csproj +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.Acceptance.IntegrationTests.csproj @@ -3,10 +3,7 @@ $(NetCurrent) false - true - enable - false - Exe + $(DefineConstants);SKIP_INTERMEDIATE_TARGET_FRAMEWORKS @@ -16,35 +13,27 @@ - - - - - + + + - - - - + + + + - - - PreserveNewest - - - PreserveNewest - - + - + + diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs new file mode 100644 index 0000000000..cac556b587 --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public class NativeAotTests : AcceptanceTestBase +{ + private readonly AcceptanceFixture _acceptanceFixture; + + public NativeAotTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) + : base(testExecutionContext) + { + _acceptanceFixture = acceptanceFixture; + } + + [NonTest] + public async Task NativeAotTests_WillRunWithExitCodeZero() + { + string testCode = """ +#file NativeAotTests.csproj + + + $TargetFramework$ + enable + enable + Exe + true + preview + true + + + + + + + + + +#file Program.cs +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; + +using Microsoft.Testing.Internal.Framework; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +using NativeAotTests; + +ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); +builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); +using ITestApplication app = await builder.BuildAsync(); +return await app.RunAsync(); + +#file TestClass1.cs +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MyTests; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void TestMethod1() + { + } + + [TestMethod] + [DataRow(0, 1)] + public void TestMethod2(int a, int b) + { + } + + [TestMethod] + [DynamicData(nameof(Data))] + public void TestMethod3(int a, int b) + { + } + + public static IEnumerable Data { get; } + = new[] + { + new object[] { 1, 2 } + }; +} +"""; + + using TestAsset generator = await TestAsset.GenerateAssetAsync( + "NativeAotTests", + testCode + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion) + .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent.Arguments) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion), + addPublicFeeds: true); + + string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + compilationResult = await DotnetCli.RunAsync( + $"publish -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", + _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + compilationResult.AssertOutputContains("Generating native code"); + string publishFolder = compilationResult.StandardOutputLines.Last().Split(" -> ")[1]; + var commandLine = new TestInfrastructure.CommandLine(); + + string exe = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "NativeAotTests.exe" : "NativeAotTests"; + int exitCode = await commandLine.RunAsyncAndReturnExitCodeAsync(Path.Combine(publishFolder, "NativeAotTests.exe")); + Assert.AreEqual(0, exitCode); + } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ParameterizedTestTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ParameterizedTestTests.cs new file mode 100644 index 0000000000..725f1a8698 --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ParameterizedTestTests.cs @@ -0,0 +1,205 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests; +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +namespace MSTest.Acceptance.IntegrationTests; + +[TestGroup] +public class ParameterizedTestTests : AcceptanceTestBase +{ + private readonly TestAssetFixture _testAssetFixture; + private const string DynamicDataAssetName = "DynamicData"; + private const string DataSourceAssetName = "DataSource"; + + // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). + public ParameterizedTestTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, + AcceptanceFixture globalFixture) + : base(testExecutionContext) + { + _testAssetFixture = testAssetFixture; + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusive_Passes(string currentTfm) + => await RunTestsAsync(currentTfm, DynamicDataAssetName, true); + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task SendingEmptyDataToDataSourceTest_WithSettingConsiderEmptyDataSourceAsInconclusive_Passes(string currentTfm) + => await RunTestsAsync(currentTfm, DataSourceAssetName, true); + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusiveToFalse_Fails(string currentTfm) + => await RunTestsAsync(currentTfm, DynamicDataAssetName, false); + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task SendingEmptyDataToDataSourceTest_WithSettingConsiderEmptyDataSourceAsInconclusiveToFalse_Fails(string currentTfm) + => await RunTestsAsync(currentTfm, DataSourceAssetName, false); + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task SendingEmptyDataToDynamicDataTest_WithoutSettingConsiderEmptyDataSourceAsInconclusive_Fails(string currentTfm) + => await RunTestsAsync(currentTfm, DynamicDataAssetName, null); + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task SendingEmptyDataToDataSourceTest_WithoutSettingConsiderEmptyDataSourceAsInconclusive_Fails(string currentTfm) + => await RunTestsAsync(currentTfm, DataSourceAssetName, null); + + private async Task RunTestsAsync(string currentTfm, string assetName, bool? isEmptyDataInconclusive) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.GetAssetPath(assetName), assetName, currentTfm); + + TestHostResult testHostResult = await testHost.ExecuteAsync(SetupRunSettingsAndGetArgs(isEmptyDataInconclusive)); + + bool isSuccess = isEmptyDataInconclusive.HasValue && isEmptyDataInconclusive.Value; + + testHostResult.AssertExitCodeIs(isSuccess ? ExitCodes.Success : ExitCodes.AtLeastOneTestFailed); + + testHostResult.AssertOutputContains(isSuccess ? "skipped Test" : "failed Test"); + + string? SetupRunSettingsAndGetArgs(bool? isEmptyDataInconclusive) + { + if (!isEmptyDataInconclusive.HasValue) + { + return null; + } + + string runSettings = $""" + + + + + + {isEmptyDataInconclusive} + + +"""; + + string runSettingsFilePath = Path.Combine(testHost.DirectoryName, $"{Guid.NewGuid():N}.runsettings"); + File.WriteAllText(runSettingsFilePath, runSettings); + return $"--settings {runSettingsFilePath}"; + } + } + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + public string DynamicDataTargetAssetPath => GetAssetPath(DynamicDataAssetName); + + public string TestSourceDataTargetAssetPath => GetAssetPath(DataSourceAssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (DynamicDataAssetName, DynamicDataAssetName, + SourceCodeDynamicData + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + yield return (DataSourceAssetName, DataSourceAssetName, + SourceCodeDataSource + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceCodeDynamicData = """ +#file DynamicData.csproj + + + + Exe + true + $TargetFrameworks$ + + + + + + + + + +#file UnitTest1.cs + +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class TestClass +{ + [TestMethod] + [DynamicData(nameof(AdditionalData))] + [DynamicData(nameof(AdditionalData2))] + public void Test(int i) + { + } + + public static IEnumerable AdditionalData => Array.Empty(); + + public static IEnumerable AdditionalData2 + { + get + { + yield return new object[] { 2 }; + } + } +} +"""; + + private const string SourceCodeDataSource = """ +#file DataSource.csproj + + + + Exe + true + $TargetFrameworks$ + latest + + + + + + + + + +#file UnitTest1.cs + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Globalization; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class TestClass +{ + [TestMethod] + [CustomTestDataSource] + [CustomEmptyTestDataSource] + public void Test(int a, int b, int c) + { + } +} + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] +public class CustomTestDataSourceAttribute : Attribute, ITestDataSource +{ + public IEnumerable GetData(MethodInfo methodInfo) => [[1, 2, 3], [4, 5, 6]]; + + public string GetDisplayName(MethodInfo methodInfo, object[] data) => data != null ? string.Format(CultureInfo.CurrentCulture, "{0} ({1})", methodInfo.Name, string.Join(",", data)) : null; +} + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] +public class CustomEmptyTestDataSourceAttribute : Attribute, ITestDataSource +{ + public IEnumerable GetData(MethodInfo methodInfo) => []; + + public string GetDisplayName(MethodInfo methodInfo, object[] data) => data != null ? string.Format(CultureInfo.CurrentCulture, "{0} ({1})", methodInfo.Name, string.Join(",", data)) : null; +} +"""; + } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Program.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Program.cs index 4d805b82c0..f2d4e27817 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Program.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Program.cs @@ -2,12 +2,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Diagnostics; -using System.Runtime.InteropServices; using Microsoft.Testing.Extensions; using Microsoft.Testing.Internal.Framework.Configurations; -using Microsoft.Testing.Platform.CommandLine; -using Microsoft.Testing.Platform.Extensions.TestHost; using MSTest.Acceptance.IntegrationTests; @@ -18,14 +15,16 @@ DotnetCli.DoNotRetry = Debugger.IsAttached; ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.TestHost.AddTestApplicationLifecycleCallbacks(sp => new GlobalTasks(sp.GetCommandLineOptions())); builder.AddTestFramework( new TestFrameworkConfiguration(Debugger.IsAttached ? 1 : Environment.ProcessorCount), new SourceGeneratedTestNodesBuilder()); -builder.AddCrashDumpProvider(); -builder.AddTrxReportProvider(); +#if ENABLE_CODECOVERAGE builder.AddCodeCoverageProvider(); +#endif +builder.AddHangDumpProvider(); +builder.AddCrashDumpProvider(ignoreIfNotSupported: true); +builder.AddTrxReportProvider(); // Custom suite tools CompositeExtensionFactory slowestTestCompositeServiceFactory @@ -36,47 +35,3 @@ CompositeExtensionFactory slowestTestCompositeServiceFacto int returnValue = await app.RunAsync(); Console.WriteLine($"Process started: {CommandLine.TotalProcessesAttempt}"); return returnValue; - -internal sealed class GlobalTasks : ITestApplicationLifecycleCallbacks -{ - private readonly ICommandLineOptions _commandLineOptions; - - public GlobalTasks(ICommandLineOptions commandLineOptions) - { - _commandLineOptions = commandLineOptions; - } - - public string Uid => nameof(GlobalTasks); - - public string Version => "1.0.0"; - - public string DisplayName => string.Empty; - - public string Description => string.Empty; - - public Task IsEnabledAsync() => Task.FromResult(true); - - public async Task AfterRunAsync(int returnValue, CancellationToken cancellationToken) - { - // Remove net462 tests from baseline on non-Windows - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - string[] allLines = File.ReadAllLines(Path.Combine(AppContext.BaseDirectory, "testsbaseline.txt")); - using FileStream fs = File.OpenWrite(Path.Combine(AppContext.BaseDirectory, "testsbaseline.notwin.txt")); - using StreamWriter sw = new(fs); - foreach (string line in allLines.Where(x => !x.Contains("net462"))) - { - await sw.WriteLineAsync(line); - } - } - - // Verify run tests are matching expected baseline - TestsRunWatchDog.BaselineFile = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? Path.Combine(AppContext.BaseDirectory, "testsbaseline.txt") - : Path.Combine(AppContext.BaseDirectory, "testsbaseline.notwin.txt"); - - await TestsRunWatchDog.VerifyAsync(skip: _commandLineOptions.IsServerMode() || Debugger.IsAttached, fixBaseLine: true); - } - - public Task BeforeRunAsync(CancellationToken cancellationToken) => Task.CompletedTask; -} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs similarity index 92% rename from test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs rename to test/IntegrationTests/MSTest.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs index 3758a0af4d..cd55ef1520 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs @@ -26,9 +26,7 @@ public async Task RunTests_ThatEnablePublishAOT_ButDontBuildToNative() AssetName, SourceCode .PatchCodeWithReplace("$TargetFramework$", $"{TargetFrameworks.NetCurrent.Arguments}") - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion), - addPublicFeeds: true); + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c Debug {generator.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); @@ -63,7 +61,6 @@ public async Task RunTests_ThatEnablePublishAOT_ButDontBuildToNative() x64 - diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunnerTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunnerTests.cs new file mode 100644 index 0000000000..741fc4825f --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunnerTests.cs @@ -0,0 +1,142 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Logging.StructuredLogger; +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; + +using SL = Microsoft.Build.Logging.StructuredLogger; +using SystemTask = System.Threading.Tasks.Task; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public class RunnerTests : AcceptanceTestBase +{ + private const string AssetName = "MSTestProject"; + private readonly AcceptanceFixture _acceptanceFixture; + + public RunnerTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) + : base(testExecutionContext) + { + _acceptanceFixture = acceptanceFixture; + } + + [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + public async SystemTask EnableMSTestRunner_True_Will_Run_Standalone(string tfm, BuildConfiguration buildConfiguration, Verb verb) + { + using TestAsset generator = await TestAsset.GenerateAssetAsync( + AssetName, + CurrentMSTestSourceCode + .PatchCodeWithReplace("$TargetFramework$", $"{tfm}") + .PatchCodeWithReplace("$MicrosoftNETTestSdkVersion$", MicrosoftNETTestSdkVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) + .PatchCodeWithReplace("$EnableMSTestRunner$", "true") + .PatchCodeWithReplace("$OutputType$", "Exe") + .PatchCodeWithReplace("$Extra$", string.Empty)); + string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + compilationResult = await DotnetCli.RunAsync( + $"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -bl:{binlogFile} -r {RID}", + _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + + SL.Build binLog = SL.Serialization.Read(binlogFile); + Assert.IsNotEmpty(binLog.FindChildrenRecursive() + .Where(x => x.Title.Contains("ProjectCapability")) + .Where(x => x.Children.Any(c => ((Item)c).Name == "TestingPlatformServer"))); + + var testHost = TestInfrastructure.TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration, verb: verb); + TestHostResult testHostResult = await testHost.ExecuteAsync(); + testHostResult.AssertOutputContains("Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1"); + } + + [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + public async SystemTask EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string tfm, BuildConfiguration buildConfiguration, Verb verb) + { + using TestAsset generator = await TestAsset.GenerateAssetAsync( + AssetName, + (CurrentMSTestSourceCode + """ +#file Program.cs + +using Microsoft.Testing.Platform.Builder; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); +builder.AddMSTest(() => new[] { typeof(Program).Assembly }); +using ITestApplication app = await builder.BuildAsync(); +return await app.RunAsync(); +""") + .PatchCodeWithReplace("$TargetFramework$", $"{tfm}") + .PatchCodeWithReplace("$MicrosoftNETTestSdkVersion$", MicrosoftNETTestSdkVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) + .PatchCodeWithReplace("$EnableMSTestRunner$", "true") + .PatchCodeWithReplace("$OutputType$", "Exe") + .PatchCodeWithReplace("$Extra$", """ +False +preview +""")); + string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); + await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync( + $"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -bl:{binlogFile} -r {RID}", + _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + var testHost = TestInfrastructure.TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration, verb: verb); + TestHostResult testHostResult = await testHost.ExecuteAsync(); + testHostResult.AssertOutputContains("Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1"); + } + + [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + public async SystemTask EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string tfm, BuildConfiguration buildConfiguration, Verb verb) + { + using TestAsset generator = await TestAsset.GenerateAssetAsync( + AssetName, + CurrentMSTestSourceCode + .PatchCodeWithReplace("$TargetFramework$", $"{tfm}") + .PatchCodeWithReplace("$MicrosoftNETTestSdkVersion$", MicrosoftNETTestSdkVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) + .PatchCodeWithReplace("$EnableMSTestRunner$", "false") + .PatchCodeWithReplace("$OutputType$", "Exe") + .PatchCodeWithReplace("$Extra$", string.Empty)); + string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + try + { + compilationResult = await DotnetCli.RunAsync($"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -bl:{binlogFile} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + var testHost = TestInfrastructure.TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration, verb: verb); + TestHostResult testHostResult = await testHost.ExecuteAsync(); + Assert.AreEqual(string.Empty, testHostResult.StandardOutput); + } + catch (Exception ex) + { + if (TargetFrameworks.NetFramework.Any(x => x.Arguments == tfm)) + { + Assert.IsTrue(ex.Message.Contains("Program does not contain a static 'Main' method suitable for an entry point"), ex.Message); + + // .NET Framework does not insert the entry point for empty program. + return; + } + } + } + + [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + public async SystemTask EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string tfm, BuildConfiguration buildConfiguration, Verb verb) + { + using TestAsset generator = await TestAsset.GenerateAssetAsync( + AssetName, + CurrentMSTestSourceCode + .PatchCodeWithReplace("$TargetFramework$", $"{tfm}") + .PatchCodeWithReplace("$MicrosoftNETTestSdkVersion$", MicrosoftNETTestSdkVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) + .PatchCodeWithReplace("$EnableMSTestRunner$", string.Empty) + .PatchCodeWithReplace("$OutputType$", string.Empty) + .PatchCodeWithReplace("$Extra$", string.Empty)); + + string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + compilationResult = await DotnetCli.RunAsync($"{verb} -bl:{binlogFile} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -r {RID} ", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + + SL.Build binLog = SL.Serialization.Read(binlogFile); + Assert.IsEmpty(binLog.FindChildrenRecursive() + .Where(x => x.Title.Contains("ProjectCapability")) + .Where(x => x.Children.Any(c => ((Item)c).Name == "TestingPlatformServer"))); + } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs index 54830520f4..20d2d80eec 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs @@ -9,7 +9,7 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] +// [TestGroup] public sealed class SdkTests : AcceptanceTestBase { private const string AssetName = "MSTestSdk"; @@ -18,6 +18,12 @@ public sealed class SdkTests : AcceptanceTestBase #file MSTestSdk.csproj + + true $OutputType$ $TargetFramework$ $EnableMSTestRunner$ @@ -71,8 +77,7 @@ public async Task RunTests_With_VSTest(string multiTfm, BuildConfiguration build .PatchCodeWithReplace("$EnableMSTestRunner$", "true") .PatchCodeWithReplace("$TestingPlatformDotnetTestSupport$", string.Empty) .PatchCodeWithReplace("$ExtraProperties$", string.Empty) - .PatchCodeWithReplace("$Extensions$", string.Empty), - addPublicFeeds: true); + .PatchCodeWithReplace("$Extensions$", string.Empty)); File.WriteAllText(Path.Combine(generator.TargetAssetPath, "Directory.Packages.props"), """ @@ -85,13 +90,15 @@ public async Task RunTests_With_VSTest(string multiTfm, BuildConfiguration build DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c {buildConfiguration} {generator.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.AreEqual(0, compilationResult.ExitCode); - compilationResult.AssertOutputRegEx(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* ms - MSTestSdk.dll \(net8\.0\)"); - compilationResult.AssertOutputRegEx(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* ms - MSTestSdk.dll \(net7\.0\)"); - compilationResult.AssertOutputRegEx(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* ms - MSTestSdk.dll \(net6\.0\)"); + compilationResult.AssertOutputRegEx(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net8\.0\)"); +#if !SKIP_INTERMEDIATE_TARGET_FRAMEWORKS + compilationResult.AssertOutputRegEx(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net7\.0\)"); + compilationResult.AssertOutputRegEx(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net6\.0\)"); +#endif if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - compilationResult.AssertOutputRegEx(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* ms - MSTestSdk.dll \(net462\)"); + compilationResult.AssertOutputRegEx(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net462\)"); } } @@ -107,8 +114,7 @@ public async Task RunTests_With_MSTestRunner_DotnetTest(string multiTfm, BuildCo .PatchCodeWithReplace("$EnableMSTestRunner$", string.Empty) .PatchCodeWithReplace("$TestingPlatformDotnetTestSupport$", string.Empty) .PatchCodeWithReplace("$ExtraProperties$", string.Empty) - .PatchCodeWithReplace("$Extensions$", string.Empty), - addPublicFeeds: true); + .PatchCodeWithReplace("$Extensions$", string.Empty)); File.WriteAllText(Path.Combine(generator.TargetAssetPath, "Directory.Packages.props"), """ @@ -122,8 +128,10 @@ public async Task RunTests_With_MSTestRunner_DotnetTest(string multiTfm, BuildCo Assert.AreEqual(0, compilationResult.ExitCode); compilationResult.AssertOutputRegEx(@"Tests succeeded: .* \[net8\.0|x64\]"); +#if !SKIP_INTERMEDIATE_TARGET_FRAMEWORKS compilationResult.AssertOutputRegEx(@"Tests succeeded: .* \[net7\.0|x64\]"); compilationResult.AssertOutputRegEx(@"Tests succeeded: .* \[net6\.0|x64\]"); +#endif if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { @@ -143,8 +151,7 @@ public async Task RunTests_With_MSTestRunner_Standalone(string multiTfm, BuildCo .PatchCodeWithReplace("$EnableMSTestRunner$", string.Empty) .PatchCodeWithReplace("$TestingPlatformDotnetTestSupport$", string.Empty) .PatchCodeWithReplace("$ExtraProperties$", string.Empty) - .PatchCodeWithReplace("$Extensions$", string.Empty), - addPublicFeeds: true); + .PatchCodeWithReplace("$Extensions$", string.Empty)); File.WriteAllText(Path.Combine(generator.TargetAssetPath, "Directory.Packages.props"), """ @@ -176,8 +183,7 @@ public async Task RunTests_With_CentralPackageManagement_Standalone(string multi .PatchCodeWithReplace("$EnableMSTestRunner$", string.Empty) .PatchCodeWithReplace("$TestingPlatformDotnetTestSupport$", string.Empty) .PatchCodeWithReplace("$ExtraProperties$", string.Empty) - .PatchCodeWithReplace("$Extensions$", string.Empty), - addPublicFeeds: true); + .PatchCodeWithReplace("$Extensions$", string.Empty)); File.WriteAllText(Path.Combine(generator.TargetAssetPath, "Directory.Packages.props"), """ @@ -187,7 +193,7 @@ public async Task RunTests_With_CentralPackageManagement_Standalone(string multi """); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {generator.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {generator.TargetAssetPath} /warnAsError", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.AreEqual(0, compilationResult.ExitCode); foreach (string tfm in multiTfm.Split(";")) { @@ -253,8 +259,7 @@ public async Task RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Exte .PatchCodeWithReplace("$EnableMSTestRunner$", string.Empty) .PatchCodeWithReplace("$TestingPlatformDotnetTestSupport$", string.Empty) .PatchCodeWithReplace("$ExtraProperties$", string.Empty) - .PatchCodeWithReplace("$Extensions$", msbuildExtensionEnableFragment), - addPublicFeeds: true); + .PatchCodeWithReplace("$Extensions$", msbuildExtensionEnableFragment)); File.WriteAllText(Path.Combine(generator.TargetAssetPath, "Directory.Packages.props"), """ @@ -264,7 +269,7 @@ public async Task RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Exte """); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {generator.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {generator.TargetAssetPath} /warnAsError", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.AreEqual(0, compilationResult.ExitCode); foreach (string tfm in multiTfm.Split(";")) { @@ -289,8 +294,7 @@ public async Task RunTests_With_MSTestRunner_Standalone_EnableAll_Extensions(str .PatchCodeWithReplace("$EnableMSTestRunner$", string.Empty) .PatchCodeWithReplace("$TestingPlatformDotnetTestSupport$", string.Empty) .PatchCodeWithReplace("$ExtraProperties$", string.Empty) - .PatchCodeWithReplace("$Extensions$", "AllMicrosoft"), - addPublicFeeds: true); + .PatchCodeWithReplace("$Extensions$", "AllMicrosoft")); File.WriteAllText(Path.Combine(generator.TargetAssetPath, "Directory.Packages.props"), """ @@ -336,8 +340,7 @@ public async Task RunTests_With_MSTestRunner_Standalone_Enable_Default_Extension .PatchCodeWithReplace("$EnableMSTestRunner$", string.Empty) .PatchCodeWithReplace("$TestingPlatformDotnetTestSupport$", string.Empty) .PatchCodeWithReplace("$ExtraProperties$", string.Empty) - .PatchCodeWithReplace("$Extensions$", enableDefaultExtensions ? string.Empty : "None"), - addPublicFeeds: true); + .PatchCodeWithReplace("$Extensions$", enableDefaultExtensions ? string.Empty : "None")); File.WriteAllText(Path.Combine(generator.TargetAssetPath, "Directory.Packages.props"), """ @@ -376,8 +379,7 @@ public async Task Invalid_TestingProfile_Name_Should_Fail(string multiTfm, Build .PatchCodeWithReplace("$EnableMSTestRunner$", string.Empty) .PatchCodeWithReplace("$TestingPlatformDotnetTestSupport$", string.Empty) .PatchCodeWithReplace("$ExtraProperties$", string.Empty) - .PatchCodeWithReplace("$Extensions$", "WrongName"), - addPublicFeeds: true); + .PatchCodeWithReplace("$Extensions$", "WrongName")); File.WriteAllText(Path.Combine(generator.TargetAssetPath, "Directory.Packages.props"), """ @@ -397,28 +399,27 @@ public async Task NativeAot_Smoke_Test_On_Windows() => // I suppose due to the load on the build machines. So, we retry the test a few times. await RetryHelper.RetryAsync( async () => - { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - return; - } - - using TestAsset generator = await TestAsset.GenerateAssetAsync( - AssetName, - SingleTestSourceCode - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) - .PatchCodeWithReplace("$OutputType$", string.Empty) - .PatchCodeWithReplace("$TargetFramework$", $"{TargetFrameworks.NetCurrent.Arguments}") - .PatchCodeWithReplace("$EnableMSTestRunner$", string.Empty) - .PatchCodeWithReplace("$TestingPlatformDotnetTestSupport$", string.Empty) - .PatchCodeWithReplace("$ExtraProperties$", """ + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + + using TestAsset generator = await TestAsset.GenerateAssetAsync( + AssetName, + SingleTestSourceCode + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) + .PatchCodeWithReplace("$OutputType$", string.Empty) + .PatchCodeWithReplace("$TargetFramework$", $"{TargetFrameworks.NetCurrent.Arguments}") + .PatchCodeWithReplace("$EnableMSTestRunner$", string.Empty) + .PatchCodeWithReplace("$TestingPlatformDotnetTestSupport$", string.Empty) + .PatchCodeWithReplace("$ExtraProperties$", """ true false """) - .PatchCodeWithReplace("$Extensions$", string.Empty), - addPublicFeeds: true); + .PatchCodeWithReplace("$Extensions$", string.Empty)); - File.WriteAllText(Path.Combine(generator.TargetAssetPath, "Directory.Packages.props"), """ + File.WriteAllText(Path.Combine(generator.TargetAssetPath, "Directory.Packages.props"), """ true @@ -426,14 +427,14 @@ await RetryHelper.RetryAsync( """); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"publish -r {RID} {generator.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - compilationResult.AssertOutputNotContains("warning"); - compilationResult.AssertOutputContains("Generating native code"); - var testHost = TestHost.LocateFrom(generator.TargetAssetPath, AssetName, TargetFrameworks.NetCurrent.Arguments, verb: Verb.publish); - TestHostResult testHostResult = await testHost.ExecuteAsync(); - testHostResult.AssertExitCodeIs(ExitCodes.Success); - testHostResult.AssertOutputContains("Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1"); - }, 3, TimeSpan.FromSeconds(5)); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"publish -r {RID} {generator.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + compilationResult.AssertOutputNotContains("warning"); + compilationResult.AssertOutputContains("Generating native code"); + var testHost = TestHost.LocateFrom(generator.TargetAssetPath, AssetName, TargetFrameworks.NetCurrent.Arguments, verb: Verb.publish); + TestHostResult testHostResult = await testHost.ExecuteAsync(); + testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertOutputContains("Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1"); + }, 3, TimeSpan.FromSeconds(5)); [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] public async Task EnablePlaywrightProperty_WhenUsingRunner_AllowsToRunPlaywrightTests(string tfm) diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/LogsCollector.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/LogsCollector.cs index f06fe3ef07..037e12b643 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/LogsCollector.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/LogsCollector.cs @@ -7,6 +7,4 @@ namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; -public class LogsCollector : ConcurrentBag -{ -} +public class LogsCollector : ConcurrentBag; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/ServerModeTestsBase.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/ServerModeTestsBase.cs index 4a5b9c349e..9cea463f82 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/ServerModeTestsBase.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/ServerModeTestsBase.cs @@ -11,8 +11,6 @@ using Microsoft.Testing.Platform.Acceptance.IntegrationTests; using Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; -using StreamJsonRpc; - namespace MSTest.Acceptance.IntegrationTests.Messages.V100; public partial /* for codegen regx */ class ServerModeTestsBase : AcceptanceTestBase @@ -37,12 +35,13 @@ protected async Task StartAsServerAndConnectToTheClientAs foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) { // Skip all unwanted environment variables. - if (WellKnownEnvironmentVariables.ToSkipEnvironmentVariables.Contains(entry.Key!.ToString(), StringComparer.OrdinalIgnoreCase)) + string? key = entry.Key.ToString(); + if (WellKnownEnvironmentVariables.ToSkipEnvironmentVariables.Contains(key, StringComparer.OrdinalIgnoreCase)) { continue; } - environmentVariables[entry.Key!.ToString()!] = entry.Value!.ToString()!; + environmentVariables[key!] = entry.Value!.ToString()!; } // We expect to not fail for unhandled exception in server mode for IDE needs. @@ -65,7 +64,7 @@ protected async Task StartAsServerAndConnectToTheClientAs IProcessHandle processHandler = ProcessFactory.Start(processConfig, cleanDefaultEnvironmentVariableIfCustomAreProvided: false); - TcpClient? tcpClient = null; + TcpClient? tcpClient; using CancellationTokenSource cancellationTokenSource = new(TimeSpan.FromSeconds(60)); try { @@ -85,12 +84,13 @@ protected async Task StartAsServerAndConnectAsync(TestHos foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) { // Skip all unwanted environment variables. - if (WellKnownEnvironmentVariables.ToSkipEnvironmentVariables.Contains(entry.Key!.ToString(), StringComparer.OrdinalIgnoreCase)) + string? key = entry.Key.ToString(); + if (WellKnownEnvironmentVariables.ToSkipEnvironmentVariables.Contains(key, StringComparer.OrdinalIgnoreCase)) { continue; } - environmentVariables[entry.Key!.ToString()!] = entry.Value!.ToString()!; + environmentVariables[key!] = entry.Value!.ToString()!; } // We expect to not fail for unhandled exception in server mode for IDE needs. @@ -98,7 +98,6 @@ protected async Task StartAsServerAndConnectAsync(TestHos // To attach to the server on startup // environmentVariables.Add(EnvironmentVariableConstants.TESTINGPLATFORM_LAUNCH_ATTACH_DEBUGGER, "1"); - TaskCompletionSource clientCreated = new(); TaskCompletionSource portFound = new(); ProcessConfiguration processConfig = new(testHost.FullName) { diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TelemetryCollector.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TelemetryCollector.cs index 75f3ce8317..ac621b10fd 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TelemetryCollector.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TelemetryCollector.cs @@ -7,6 +7,4 @@ namespace MSTest.Acceptance.IntegrationTests.Messages.V100; -public class TelemetryCollector : ConcurrentBag -{ -} +public class TelemetryCollector : ConcurrentBag; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TestNodeUpdateCollector.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TestNodeUpdateCollector.cs index 10df81255c..b9eb77b1b9 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TestNodeUpdateCollector.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TestNodeUpdateCollector.cs @@ -12,7 +12,7 @@ public class TestNodeUpdateCollector private readonly TaskCompletionSource _taskCompletionSource = new(); private readonly Func? _completeCollector; - public ConcurrentBag TestNodeUpdates { get; private set; } = new(); + public ConcurrentBag TestNodeUpdates { get; } = new(); public TestNodeUpdateCollector(Func? completeCollectorWhen = null) { diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/TelemetryPayload.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/TelemetryPayload.cs index 003c456a3d..dc5ebb435e 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/TelemetryPayload.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/TelemetryPayload.cs @@ -5,7 +5,7 @@ namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; -public record class TelemetryPayload +public record TelemetryPayload ( [property: JsonProperty(nameof(TelemetryPayload.EventName))] string EventName, diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/TestingPlatformClient.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/TestingPlatformClient.cs index 30b6d79f89..6f2a5c68c0 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/TestingPlatformClient.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/TestingPlatformClient.cs @@ -156,7 +156,7 @@ public void Dispose() _processHandler.Dispose(); } - public record class Log(LogLevel LogLevel, string Message); + public record Log(LogLevel LogLevel, string Message); private sealed class TargetHandler { diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestDiscoveryTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestDiscoveryTests.cs index 7cc4388f2e..a5b7e96287 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestDiscoveryTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestDiscoveryTests.cs @@ -56,7 +56,6 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test SourceCode .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); } @@ -73,7 +72,6 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test - diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestFilterTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestFilterTests.cs new file mode 100644 index 0000000000..d6f5181fd1 --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestFilterTests.cs @@ -0,0 +1,147 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests; +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +namespace MSTest.Acceptance.IntegrationTests; + +[TestGroup] +public class TestFilterTests : AcceptanceTestBase +{ + private readonly TestAssetFixture _testAssetFixture; + private const string AssetName = "TestFilter"; + + // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). + public TestFilterTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, + AcceptanceFixture globalFixture) + : base(testExecutionContext) + { + _testAssetFixture = testAssetFixture; + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task RunWithFilter_UsingTestProperty_FilteredTests(string currentTfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + + TestHostResult testHostResult = await testHost.ExecuteAsync("--filter tree=one"); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertOutputContains("Passed: 1, Skipped: 0, Total: 1,"); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task DiscoverTestsWithFilter_UsingTestProperty_FilteredTests(string currentTfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + + TestHostResult testHostResult = await testHost.ExecuteAsync("--filter tree=one --list-tests"); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertOutputContains(""" +The following Tests are available: +Test2 +"""); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task UsingTestPropertyForOwnerAndPriorityAndTestCategory_TestsFailed(string currentTfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + + TestHostResult testHostResult = await testHost.ExecuteAsync("--filter tree!~one"); + + testHostResult.AssertOutputContains(""" +failed PriorityTest 0ms +UTA023: TestClass: Cannot define predefined property Priority on method PriorityTest. +failed OwnerTest 0ms +UTA023: TestClass: Cannot define predefined property Owner on method OwnerTest. +failed TestCategoryTest 0ms +UTA023: TestClass: Cannot define predefined property TestCategory on method TestCategoryTest. +"""); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task RunWithFilter_UsingTestPropertyForOwner_FilteredButTestsFailed(string currentTfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + + TestHostResult testHostResult = await testHost.ExecuteAsync("--filter owner=testOwner"); + + testHostResult.AssertOutputContains(""" +failed OwnerTest 0ms +UTA023: TestClass: Cannot define predefined property Owner on method OwnerTest. +"""); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task RunWithFilter_UsingTestPropertyForPriorityAndTestCategory_NotFiltered(string currentTfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + + TestHostResult testHostResult = await testHost.ExecuteAsync("--filter TestCategory=category|Priority=1"); + + testHostResult.AssertOutputContains("Zero tests ran"); + testHostResult.AssertExitCodeIs(8); + } + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + public string TargetAssetPath => GetAssetPath(AssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (AssetName, AssetName, + SourceCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceCode = """ +#file TestFilter.csproj + + + + Exe + true + $TargetFrameworks$ + + + + + + + + + +#file UnitTest1.cs + +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class TestClass +{ + [TestMethod] + [TestProperty("tree", "one")] + public void Test2() { } + + [TestMethod] + [TestProperty("Priority", "1")] + public void PriorityTest() { } + + [TestMethod] + [TestProperty("Owner", "testOwner")] + public void OwnerTest() { } + + [TestMethod] + [TestProperty("TestCategory", "category")] + public void TestCategoryTest() { } +} +"""; + } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestRunParametersTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestRunParametersTests.cs new file mode 100644 index 0000000000..6371e40d02 --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestRunParametersTests.cs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests; +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; + +namespace MSTest.Acceptance.IntegrationTests; + +[TestGroup] +public sealed class TestRunParametersTests : AcceptanceTestBase +{ + private readonly TestAssetFixture _testAssetFixture; + + public TestRunParametersTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) + : base(testExecutionContext) + { + _testAssetFixture = testAssetFixture; + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task TestRunParameters_WhenProvidingMultipleArgumentsToTheOption(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings --test-parameter MyParameter2=MyValue2 MyParameter3=MyValue3"); + + // Assert + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContains("Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1"); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task TestRunParameters_WhenProvidingMultipleMultipleTimesTheOptionAndArgument(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings --test-parameter MyParameter2=MyValue2 --test-parameter MyParameter3=MyValue3"); + + // Assert + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContains("Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1"); + } + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + public const string ProjectName = "TestRunParameters"; + + public string ProjectPath => GetAssetPath(ProjectName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (ProjectName, ProjectName, + SourceCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceCode = """ +#file TestRunParameters.csproj + + + + Exe + true + $TargetFrameworks$ + + + + + + + + + + PreserveNewest + + + + + +#file my.runsettings + + + + + + + +#file UnitTest1.cs +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class UnitTest1 +{ + public TestContext TestContext { get; set; } + + [TestMethod] + public void MyTestMethod() + { + Assert.AreEqual("MyValue1", TestContext.Properties["MyParameter1"]); + Assert.AreEqual("MyValue2", TestContext.Properties["MyParameter2"]); + Assert.AreEqual("MyValue3", TestContext.Properties["MyParameter3"]); + } +} +"""; + } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadContextTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadContextTests.cs index ec63a5fe99..5e30b21cb6 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadContextTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadContextTests.cs @@ -20,7 +20,7 @@ public ThreadContextTests(ITestExecutionContext testExecutionContext, TestAssetF [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] public async Task ThreadingContext_WhenCultureIsNotSet_TestMethodFails(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + var testHost = TestHost.LocateFrom(_testAssetFixture.InitToTestProjectPath, TestAssetFixture.InitToTestProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertOutputContains("Failed: 1, Passed: 0, Skipped: 0, Total: 1"); } @@ -39,19 +39,111 @@ public async Task ThreadingContext_WhenChangedInTestInitialize_IsPassedToTestMet private async Task SetCultureInFixtureMethodAndRunTests(string tfm, string envVarKey) { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { [envVarKey] = "1" }); + var testHost = TestHost.LocateFrom(_testAssetFixture.InitToTestProjectPath, TestAssetFixture.InitToTestProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { [envVarKey] = "true" }); testHostResult.AssertExitCodeIs(0); testHostResult.AssertOutputContains("Failed: 0, Passed: 1, Skipped: 0, Total: 1"); } + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task ThreadingContext_CurrentCultureFlowsBetweenMethods(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsProjectPath, TestAssetFixture.CultureFlowsProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(); + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContains("Failed: 0, Passed: 1, Skipped: 0, Total: 1"); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task ThreadingContext_WhenUsingSTAThread_CurrentCultureFlowsBetweenMethods(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsProjectPath, TestAssetFixture.CultureFlowsProjectName, tfm); + string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta.runsettings"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}"); + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContains("Failed: 0, Passed: 1, Skipped: 0, Total: 1"); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task ThreadingContext_WhenUsingSTAThreadAndTimeout_CurrentCultureFlowsBetweenMethods(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsProjectPath, TestAssetFixture.CultureFlowsProjectName, tfm); + string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta-timeout.runsettings"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() + { + ["MSTEST_TEST_FLOW_CONTEXT"] = "true", + }); + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContains("Failed: 0, Passed: 1, Skipped: 0, Total: 1"); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task ThreadingContext_WhenUsingTimeout_CurrentCultureFlowsBetweenMethods(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsProjectPath, TestAssetFixture.CultureFlowsProjectName, tfm); + string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "timeout.runsettings"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() + { + ["MSTEST_TEST_FLOW_CONTEXT"] = "true", + }); + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContains("Failed: 0, Passed: 1, Skipped: 0, Total: 1"); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task ThreadingContext_Inheritance_CurrentCultureFlowsBetweenMethods(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsInheritanceProjectPath, TestAssetFixture.CultureFlowsInheritanceProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(); + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContains("Failed: 0, Passed: 8, Skipped: 0, Total: 8"); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task ThreadingContext_Inheritance_WhenUsingSTAThread_CurrentCultureFlowsBetweenMethods(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsInheritanceProjectPath, TestAssetFixture.CultureFlowsInheritanceProjectName, tfm); + string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta.runsettings"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}"); + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContains("Failed: 0, Passed: 8, Skipped: 0, Total: 8"); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task ThreadingContext_Inheritance_WhenUsingSTAThreadAndTimeout_CurrentCultureFlowsBetweenMethods(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsInheritanceProjectPath, TestAssetFixture.CultureFlowsInheritanceProjectName, tfm); + string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta-timeout.runsettings"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() + { + ["MSTEST_TEST_FLOW_CONTEXT"] = "true", + }); + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContains("Failed: 0, Passed: 8, Skipped: 0, Total: 8"); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task ThreadingContext_Inheritance_WhenUsingTimeout_CurrentCultureFlowsBetweenMethods(string tfm) + { + var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsInheritanceProjectPath, TestAssetFixture.CultureFlowsInheritanceProjectName, tfm); + string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "timeout.runsettings"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() + { + ["MSTEST_TEST_FLOW_CONTEXT"] = "true", + }); + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContains("Failed: 0, Passed: 8, Skipped: 0, Total: 8"); + } + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) { - public const string ProjectName = "ThreadContextProject"; - private const string SourceCode = """ -#file ThreadContextProject.csproj + public const string InitToTestProjectName = "InitToTestThreadContextProject"; + public const string CultureFlowsProjectName = "CultureFlowsThreadContextProject"; + public const string CultureFlowsInheritanceProjectName = "CultureFlowsInheritanceThreadContextProject"; + private const string InitToTestSourceCode = """ +#file InitToTestThreadContextProject.csproj @@ -64,13 +156,12 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) - #file UnitTest1.cs -namespace ThreadContextProject; +namespace InitToTestThreadContextProject; using System; using System.Globalization; @@ -85,7 +176,7 @@ public class UnitTest1 [AssemblyInitialize] public static void AssemblyInitialize(TestContext context) { - if (Environment.GetEnvironmentVariable("MSTEST_TEST_SET_CULTURE_ASSEMBLY_INIT") == "1") + if (Environment.GetEnvironmentVariable("MSTEST_TEST_SET_CULTURE_ASSEMBLY_INIT") == "true") { CultureInfo.CurrentCulture = new CultureInfo(CultureCodeName); } @@ -94,7 +185,7 @@ public static void AssemblyInitialize(TestContext context) [ClassInitialize] public static void ClassInitialize(TestContext context) { - if (Environment.GetEnvironmentVariable("MSTEST_TEST_SET_CULTURE_CLASS_INIT") == "1") + if (Environment.GetEnvironmentVariable("MSTEST_TEST_SET_CULTURE_CLASS_INIT") == "true") { CultureInfo.CurrentCulture = new CultureInfo(CultureCodeName); } @@ -103,7 +194,7 @@ public static void ClassInitialize(TestContext context) [TestInitialize] public void TestInitialize() { - if (Environment.GetEnvironmentVariable("MSTEST_TEST_SET_CULTURE_TEST_INIT") == "1") + if (Environment.GetEnvironmentVariable("MSTEST_TEST_SET_CULTURE_TEST_INIT") == "true") { CultureInfo.CurrentCulture = new CultureInfo(CultureCodeName); } @@ -117,12 +208,568 @@ public void TestMethod1() } """; - public string ProjectPath => GetAssetPath(ProjectName); + private const string CultureFlowsSourceCode = """ +#file sta.runsettings + + + + STA + + + +#file sta-timeout.runsettings + + + + STA + + + 10001 + 10002 + 10003 + 30004 + 10005 + 10006 + 10007 + + + +#file timeout.runsettings + + + + 10001 + 10002 + 10003 + 30004 + 10005 + 10006 + 10007 + + + +#file CultureFlowsThreadContextProject.csproj + + + + Exe + true + $TargetFrameworks$ + latest + + + + + + + + + + PreserveNewest + + + + + +#file UnitTest1.cs +namespace CultureFlowsThreadContextProject; + +using System; +using System.Globalization; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class UnitTest1 +{ + private const string AssemblyInitCultureCodeName = "th-TH"; + private const string ClassInitCultureCodeName = "tr-TR"; + private const string TestInitCultureCodeName = "sv-SE"; + private const string TestMethodCultureCodeName = "ak-GH"; + private const string TestCleanupCultureCodeName = "pt-BR"; + private const string ClassCleanupCultureCodeName = "hu-HU"; + + [AssemblyInitialize] + public static void AssemblyInitialize(TestContext context) + { + CultureInfo.CurrentCulture = new CultureInfo(AssemblyInitCultureCodeName); + } + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + Assert.AreEqual(AssemblyInitCultureCodeName, CultureInfo.CurrentCulture.Name, + "ClassInitialize culture should have been the one set by AssemblyInitialize"); + CultureInfo.CurrentCulture = new CultureInfo(ClassInitCultureCodeName); + } + + [TestInitialize] + public void TestInitialize() + { + Assert.AreEqual(ClassInitCultureCodeName, CultureInfo.CurrentCulture.Name, + "TestInitialize culture should have been the one set by ClassInitialize"); + CultureInfo.CurrentCulture = new CultureInfo(TestInitCultureCodeName); + } + + [TestMethod] + public void TestMethod1() + { + Assert.AreEqual(TestInitCultureCodeName, CultureInfo.CurrentCulture.Name, + "TestMethod culture should have been the one set by TestInitialize"); + CultureInfo.CurrentCulture = new CultureInfo(TestMethodCultureCodeName); + } + + [TestCleanup] + public void TestCleanup() + { + Assert.AreEqual(TestMethodCultureCodeName, CultureInfo.CurrentCulture.Name, + "TestCleanup culture should have been the one set by TestMethod"); + CultureInfo.CurrentCulture = new CultureInfo(TestCleanupCultureCodeName); + } + + [ClassCleanup] + public static void ClassCleanup() + { + if (Environment.GetEnvironmentVariable("MSTEST_TEST_FLOW_CONTEXT") == "true") + { + Assert.AreEqual(ClassInitCultureCodeName, CultureInfo.CurrentCulture.Name, + "ClassCleanup culture should have been the one set by ClassInitialize"); + } + else + { + Assert.AreEqual(TestCleanupCultureCodeName, CultureInfo.CurrentCulture.Name, + "ClassCleanup culture should have been the one set by TestCleanup"); + CultureInfo.CurrentCulture = new CultureInfo(ClassCleanupCultureCodeName); + } + } + + [AssemblyCleanup] + public static void AssemblyCleanup() + { + if (Environment.GetEnvironmentVariable("MSTEST_TEST_FLOW_CONTEXT") == "true") + { + Assert.AreEqual(AssemblyInitCultureCodeName, CultureInfo.CurrentCulture.Name, + "AssemblyCleanup culture should have been the one set by AssemblyInitialize"); + } + else + { + Assert.AreEqual(ClassCleanupCultureCodeName, CultureInfo.CurrentCulture.Name, + "AssemblyCleanup culture should have been the one set by ClassCleanup"); + } + } +} +"""; + + private const string CultureFlowsInheritanceSourceCode = """ +#file sta.runsettings + + + + STA + + + +#file sta-timeout.runsettings + + + + STA + + + 10001 + 10002 + 10003 + 30004 + 10005 + 10006 + 10007 + + + +#file timeout.runsettings + + + + 10001 + 10002 + 10003 + 30004 + 10005 + 10006 + 10007 + + + +#file CultureFlowsInheritanceThreadContextProject.csproj + + + + Exe + true + $TargetFrameworks$ + latest + + + + + + + + + + PreserveNewest + + + + + +#file UnitTest1.cs +namespace CultureFlowsExtraCasesThreadContextProject; + +using System; +using System.Globalization; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +public class ExpectedCultures +{ + public const string BaseClassInitCulture = "fr-FR"; + public const string BaseTestInitCulture = "hr-BA"; + public const string IntermediateClassInitCulture = "es-ES"; + public const string IntermediateTestInitCulture = "et-EE"; + public const string IntermediateTestCleanupCulture = "hu-HU"; + public const string IntermediateClassCleanupCulture = "vi-VN"; + public const string TestMethodCulture = "it-IT"; +} + +public class BaseClassWithInheritance +{ + private static TestContext _testContext; + + [ClassInitialize(InheritanceBehavior.BeforeEachDerivedClass)] + public static void BaseClassInitialize(TestContext testContext) + { + if (_testContext is not null) + { + throw new InvalidOperationException($"Was expected to be running tests sequentially but '{_testContext.ManagedMethod}' is still running when we received '{testContext.ManagedMethod}'"); + } + + _testContext = testContext; + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.BaseClassInitCulture); + } + + [ClassCleanup(InheritanceBehavior.BeforeEachDerivedClass, ClassCleanupBehavior.EndOfClass)] + public static void BaseClassCleanup() + { + switch (_testContext.ManagedMethod) + { + case "DerivedClassIntermediateClassWithoutInheritanceBaseClassWithInheritanceTestMethod": + if (Environment.GetEnvironmentVariable("MSTEST_TEST_FLOW_CONTEXT") == "true") + { + Assert.AreEqual(ExpectedCultures.BaseClassInitCulture, CultureInfo.CurrentCulture.Name); + } + else + { + Assert.AreEqual(ExpectedCultures.TestMethodCulture, CultureInfo.CurrentCulture.Name); + } + break; + + case "DerivedClassIntermediateClassWithInheritanceBaseClassWithInheritanceTestMethod": + Assert.AreEqual(ExpectedCultures.IntermediateClassCleanupCulture, CultureInfo.CurrentCulture.Name); + break; + + default: + throw new NotSupportedException($"Unsupported method name '{_testContext.ManagedMethod}'"); + } + + _testContext = null; + } +} + +public class BaseClassWithoutInheritance +{ + [ClassInitialize(InheritanceBehavior.None)] + public static void BaseClassInitialize(TestContext testContext) + { + Assert.Fail("BaseClassWithoutInheritance.BaseClassInitialize should not have been called"); + } + + [ClassCleanup(InheritanceBehavior.None)] + public static void BaseClassCleanup() + { + Assert.Fail("BaseClassWithoutInheritance.BaseClassCleanup should not have been called"); + } +} + +public class IntermediateClassWithInheritanceBaseClassWithInheritance : BaseClassWithInheritance +{ + [ClassInitialize(InheritanceBehavior.BeforeEachDerivedClass)] + public static void IntermediateClassInitialize(TestContext testContext) + { + Assert.AreEqual(ExpectedCultures.BaseClassInitCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.IntermediateClassInitCulture); + } + + [ClassCleanup(InheritanceBehavior.BeforeEachDerivedClass, ClassCleanupBehavior.EndOfClass)] + public static void IntermediateClassCleanup() + { + if (Environment.GetEnvironmentVariable("MSTEST_TEST_FLOW_CONTEXT") == "true") + { + Assert.AreEqual(ExpectedCultures.IntermediateClassInitCulture, CultureInfo.CurrentCulture.Name); + } + else + { + Assert.AreEqual(ExpectedCultures.TestMethodCulture, CultureInfo.CurrentCulture.Name); + } + + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.IntermediateClassCleanupCulture); + } +} + +public class IntermediateClassWithInheritanceBaseClassWithoutInheritance : BaseClassWithoutInheritance +{ + [ClassInitialize(InheritanceBehavior.BeforeEachDerivedClass)] + public static void IntermediateClassInitialize(TestContext testContext) + { + Assert.AreNotEqual(ExpectedCultures.BaseClassInitCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.IntermediateClassInitCulture); + } + + [ClassCleanup(InheritanceBehavior.BeforeEachDerivedClass, ClassCleanupBehavior.EndOfClass)] + public static void IntermediateClassCleanup() + { + if (Environment.GetEnvironmentVariable("MSTEST_TEST_FLOW_CONTEXT") == "true") + { + Assert.AreEqual(ExpectedCultures.IntermediateClassInitCulture, CultureInfo.CurrentCulture.Name); + } + else + { + Assert.AreEqual(ExpectedCultures.TestMethodCulture, CultureInfo.CurrentCulture.Name); + } + + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.IntermediateClassCleanupCulture); + } +} + +public class IntermediateClassWithoutInheritanceBaseClassWithInheritance : BaseClassWithInheritance +{ + [ClassInitialize(InheritanceBehavior.None)] + public static void IntermediateClassInitialize(TestContext testContext) + { + Assert.Fail("IntermediateClassWithoutInheritanceBaseClassWithInheritance.IntermediateClassInitialize should not have been called"); + } + + [ClassCleanup(InheritanceBehavior.None)] + public static void IntermediateClassCleanup() + { + Assert.Fail("IntermediateClassWithoutInheritanceBaseClassWithInheritance.IntermediateClassCleanup should not have been called"); + } +} + +public class IntermediateClassWithoutInheritanceBaseClassWithoutInheritance : BaseClassWithoutInheritance +{ + [ClassInitialize(InheritanceBehavior.None)] + public static void IntermediateClassInitialize(TestContext testContext) + { + Assert.Fail("IntermediateClassWithoutInheritanceBaseClassWithoutInheritance.IntermediateClassInitialize should not have been called"); + } + + [ClassCleanup(InheritanceBehavior.None)] + public static void IntermediateClassCleanup() + { + Assert.Fail("IntermediateClassWithoutInheritanceBaseClassWithoutInheritance.IntermediateClassCleanup should not have been called"); + } +} + +[TestClass] +public class DerivedClassIntermediateClassWithInheritanceBaseClassWithInheritance : IntermediateClassWithInheritanceBaseClassWithInheritance +{ + [TestMethod] + public void DerivedClassIntermediateClassWithInheritanceBaseClassWithInheritanceTestMethod() + { + Assert.AreEqual(ExpectedCultures.IntermediateClassInitCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.TestMethodCulture); + } +} + +[TestClass] +public class DerivedClassIntermediateClassWithInheritanceBaseClassWithoutInheritance : IntermediateClassWithInheritanceBaseClassWithoutInheritance +{ + [TestMethod] + public void DerivedClassIntermediateClassWithInheritanceBaseClassWithoutInheritanceTestMethod() + { + Assert.AreEqual(ExpectedCultures.IntermediateClassInitCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.TestMethodCulture); + } +} + +[TestClass] +public class DerivedClassIntermediateClassWithoutInheritanceBaseClassWithInheritance : IntermediateClassWithoutInheritanceBaseClassWithInheritance +{ + [TestMethod] + public void DerivedClassIntermediateClassWithoutInheritanceBaseClassWithInheritanceTestMethod() + { + Assert.AreEqual(ExpectedCultures.BaseClassInitCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.TestMethodCulture); + } +} + +[TestClass] +public class DerivedClassIntermediateClassWithoutInheritanceBaseClassWithoutInheritance : IntermediateClassWithoutInheritanceBaseClassWithoutInheritance +{ + [TestMethod] + public void DerivedClassIntermediateClassWithoutInheritanceBaseClassWithoutInheritanceTestMethod() + { + Assert.AreNotEqual(ExpectedCultures.IntermediateClassInitCulture, CultureInfo.CurrentCulture.Name); + Assert.AreNotEqual(ExpectedCultures.BaseClassInitCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.TestMethodCulture); + } +} + +public class BaseClassWithTestInitCleanup +{ + public TestContext TestContext { get; set; } + + [TestInitialize] + public void BaseTestInitialize() + { + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.BaseTestInitCulture); + } + + [TestCleanup] + public void BaseTestCleanup() + { + switch (TestContext.ManagedMethod) + { + case "DerivedClassIntermediateClassWithTestInitCleanupBaseClassWithTestInitCleanupTestMethod": + Assert.AreEqual(ExpectedCultures.IntermediateTestCleanupCulture, CultureInfo.CurrentCulture.Name); + break; + + case "DerivedClassIntermediateClassWithoutTestInitCleanupBaseClassWithTestInitCleanupTestMethod": + Assert.AreEqual(ExpectedCultures.TestMethodCulture, CultureInfo.CurrentCulture.Name); + break; + + default: + throw new NotSupportedException($"Unsupported method name '{TestContext.ManagedMethod}'"); + } + } +} + +public class BaseClassWithoutTestInitCleanup +{ +} + +public class IntermediateClassWithTestInitCleanupBaseClassWithTestInitCleanup : BaseClassWithTestInitCleanup +{ + [TestInitialize] + public void IntermediateTestInitialize() + { + Assert.AreEqual(ExpectedCultures.BaseTestInitCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.IntermediateTestInitCulture); + } + + [TestCleanup] + public void IntermediateTestCleanup() + { + Assert.AreEqual(ExpectedCultures.TestMethodCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.IntermediateTestCleanupCulture); + } +} + +public class IntermediateClassWithoutTestInitCleanupBaseClassWithTestInitCleanup : BaseClassWithTestInitCleanup +{ +} + +public class IntermediateClassWithTestInitCleanupBaseClassWithoutTestInitCleanup : BaseClassWithoutTestInitCleanup +{ + [TestInitialize] + public void IntermediateTestInitialize() + { + Assert.AreNotEqual(ExpectedCultures.BaseTestInitCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.IntermediateTestInitCulture); + } + + [TestCleanup] + public void IntermediateTestCleanup() + { + Assert.AreEqual(ExpectedCultures.TestMethodCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.IntermediateTestCleanupCulture); + } +} + +public class IntermediateClassWithoutTestInitCleanupBaseClassWithoutTestInitCleanup : BaseClassWithoutTestInitCleanup +{ +} + +[TestClass] +public class DerivedClassIntermediateClassWithTestInitCleanupBaseClassWithTestInitCleanup : IntermediateClassWithTestInitCleanupBaseClassWithTestInitCleanup +{ + [TestMethod] + public void DerivedClassIntermediateClassWithTestInitCleanupBaseClassWithTestInitCleanupTestMethod() + { + Assert.AreEqual(ExpectedCultures.IntermediateTestInitCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.TestMethodCulture); + } +} + + +[TestClass] +public class DerivedClassIntermediateClassWithTestInitCleanupBaseClassWithoutTestInitCleanup : IntermediateClassWithTestInitCleanupBaseClassWithoutTestInitCleanup +{ + [TestMethod] + public void DerivedClassIntermediateClassWithTestInitCleanupBaseClassWithoutTestInitCleanupTestMethod() + { + Assert.AreEqual(ExpectedCultures.IntermediateTestInitCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.TestMethodCulture); + } +} + +[TestClass] +public class DerivedClassIntermediateClassWithoutTestInitCleanupBaseClassWithTestInitCleanup : IntermediateClassWithoutTestInitCleanupBaseClassWithTestInitCleanup +{ + [TestMethod] + public void DerivedClassIntermediateClassWithoutTestInitCleanupBaseClassWithTestInitCleanupTestMethod() + { + Assert.AreEqual(ExpectedCultures.BaseTestInitCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.TestMethodCulture); + } +} + +[TestClass] +public class DerivedClassIntermediateClassWithoutTestInitCleanupBaseClassWithoutTestInitCleanup : IntermediateClassWithoutTestInitCleanupBaseClassWithoutTestInitCleanup +{ + [TestMethod] + public void DerivedClassIntermediateClassWithoutTestInitCleanupBaseClassWithoutTestInitCleanupTestMethod() + { + Assert.AreNotEqual(ExpectedCultures.IntermediateTestInitCulture, CultureInfo.CurrentCulture.Name); + Assert.AreNotEqual(ExpectedCultures.BaseTestInitCulture, CultureInfo.CurrentCulture.Name); + CultureInfo.CurrentCulture = new CultureInfo(ExpectedCultures.TestMethodCulture); + } +} +"""; + + public string InitToTestProjectPath => GetAssetPath(InitToTestProjectName); + + public string CultureFlowsProjectPath => GetAssetPath(CultureFlowsProjectName); + + public string CultureFlowsInheritanceProjectPath => GetAssetPath(CultureFlowsInheritanceProjectName); public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() { - yield return (ProjectName, ProjectName, - SourceCode + yield return (InitToTestProjectName, InitToTestProjectName, + InitToTestSourceCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + + yield return (CultureFlowsProjectName, CultureFlowsProjectName, + CultureFlowsSourceCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + + yield return (CultureFlowsInheritanceProjectName, CultureFlowsInheritanceProjectName, + CultureFlowsInheritanceSourceCode .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadingTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadingTests.cs index 4cbe41b974..9442d236f8 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadingTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadingTests.cs @@ -180,6 +180,25 @@ public async Task LifecycleAttributesTaskThreading_WhenMainIsNotSTA_RunsettingsA testHostResult.AssertOutputContains("Passed!"); } + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task LifecycleAttributesTaskThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA_With_ParallelAttribute(string tfm) + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + + var testHost = TestHost.LocateFrom(_testAssetFixture.LifecycleAttributesTaskProjectPath, TestAssetFixture.LifecycleWithParallelAttributesTaskProjectName, tfm); + string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta.runsettings"); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() + { + ["MSTEST_THREAD_STATE_IS_STA"] = "1", + }); + + testHostResult.AssertExitCodeIs(0); + testHostResult.AssertOutputContains("Passed!"); + } + [ArgumentsProvider(nameof(TargetFrameworks.Net), typeof(TargetFrameworks))] public async Task LifecycleAttributesValueTaskThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string tfm) { @@ -207,6 +226,7 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) public const string STAThreadProjectName = "STATestThreading"; public const string LifecycleAttributesVoidProjectName = "LifecycleAttributesVoid"; public const string LifecycleAttributesTaskProjectName = "LifecycleAttributesTask"; + public const string LifecycleWithParallelAttributesTaskProjectName = "LifecycleAttributesTask"; public const string LifecycleAttributesValueTaskProjectName = "LifecycleAttributesValueTask"; public string ProjectPath => GetAssetPath(ProjectName); @@ -217,6 +237,8 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) public string LifecycleAttributesTaskProjectPath => GetAssetPath(LifecycleAttributesTaskProjectName); + public string LifecycleWithParallelAttributesTaskProjectNamePath => GetAssetPath(LifecycleWithParallelAttributesTaskProjectName); + public string LifecycleAttributesValueTaskProjectPath => GetAssetPath(LifecycleAttributesValueTaskProjectName); public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() @@ -246,6 +268,14 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) yield return (LifecycleAttributesTaskProjectName, LifecycleAttributesTaskProjectName, LifecycleAttributesTaskSource .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$ParallelAttribute$", string.Empty) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + + yield return (LifecycleWithParallelAttributesTaskProjectName, LifecycleWithParallelAttributesTaskProjectName, + LifecycleAttributesTaskSource + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$ParallelAttribute$", "[assembly: Parallelize(Workers = 0, Scope = ExecutionScope.MethodLevel)]") .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); @@ -287,7 +317,6 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) - @@ -413,7 +442,6 @@ public static int Main(string[] args) - @@ -523,7 +551,6 @@ private static void AssertCorrectThreadApartmentState() - @@ -543,6 +570,7 @@ private static void AssertCorrectThreadApartmentState() using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; +$ParallelAttribute$ [TestClass] public class LifecycleAttributesTask { @@ -639,7 +667,6 @@ private static void AssertCorrectThreadApartmentState() - diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Usings.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Usings.cs deleted file mode 100644 index 1ec60d47a9..0000000000 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Usings.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -global using Microsoft.Testing.Internal.Framework; -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Platform.Extensions; -global using Microsoft.Testing.Platform.Services; -global using Microsoft.Testing.TestInfrastructure; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ValueTaskTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ValueTaskTests.cs index 2003199376..55b04cee61 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ValueTaskTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ValueTaskTests.cs @@ -23,8 +23,8 @@ public async Task CanUseValueTaskForAllKnownLocations() TestHostResult testHostResult = await testHost.ExecuteAsync(); // Assert - testHostResult.AssertExitCodeIs(0); - testHostResult.AssertOutputContains("Passed! - Failed: 0, Passed: 2, Skipped: 0, Total: 2"); + testHostResult.AssertExitCodeIs(2); + testHostResult.AssertOutputContains("Failed! - Failed: 1, Passed: 2, Skipped: 1, Total: 4"); } [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] @@ -56,7 +56,6 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test - @@ -91,6 +90,20 @@ public class UnitTest1 [TestMethod] public ValueTask TestMethod2() => ValueTask.CompletedTask; + + [TestMethod] + public async ValueTask FailedTestMethod() + { + await ValueTask.CompletedTask; + Assert.Fail(); + } + + [TestMethod] + public async ValueTask InconclusiveTestMethod() + { + await ValueTask.CompletedTask; + Assert.Inconclusive(); + } } """; } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/testsbaseline.txt b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/testsbaseline.txt deleted file mode 100644 index 9aefa86b38..0000000000 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/testsbaseline.txt +++ /dev/null @@ -1,248 +0,0 @@ -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.AssemblyResolutionTests.AssemblyResolution_WhenNotSpecified_TestFails() -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.AssemblyResolutionTests.AssemblyResolution_WhenSpecified_TestSucceeds() -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.AssemblyResolverTests.RunningTests_DoesNotHitResourceRecursionIssueAndDoesNotCrashTheRunner() -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.DynamicDataTests.SendingEmptyDataToDynamicDataTest_WithoutSettingConsiderEmptyDataSourceAsInconclusive_Fails(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.DynamicDataTests.SendingEmptyDataToDynamicDataTest_WithoutSettingConsiderEmptyDataSourceAsInconclusive_Fails(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.DynamicDataTests.SendingEmptyDataToDynamicDataTest_WithoutSettingConsiderEmptyDataSourceAsInconclusive_Fails(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.DynamicDataTests.SendingEmptyDataToDynamicDataTest_WithoutSettingConsiderEmptyDataSourceAsInconclusive_Fails(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.DynamicDataTests.SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusive_Passes(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.DynamicDataTests.SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusive_Passes(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.DynamicDataTests.SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusive_Passes(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.DynamicDataTests.SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusive_Passes(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.DynamicDataTests.SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusiveToFalse_Fails(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.DynamicDataTests.SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusiveToFalse_Fails(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.DynamicDataTests.SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusiveToFalse_Fails(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.DynamicDataTests.SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusiveToFalse_Fails(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InfoTests.UsingInfoArgument_OutputCorrectVersionForMSTest(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InfoTests.UsingInfoArgument_OutputCorrectVersionForMSTest(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InfoTests.UsingInfoArgument_OutputCorrectVersionForMSTest(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InfoTests.UsingInfoArgument_OutputCorrectVersionForMSTest(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyCleanup_WhenTimeoutExpires_AssemblyCleanupIsCancelled_AttributeTakesPrecedence(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyCleanup_WhenTimeoutExpires_AssemblyCleanupIsCancelled_AttributeTakesPrecedence(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyCleanup_WhenTimeoutExpires_AssemblyCleanupIsCancelled_AttributeTakesPrecedence(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyCleanup_WhenTimeoutExpires_AssemblyCleanupIsCancelled_AttributeTakesPrecedence(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyCleanup_WhenTimeoutExpires_AssemblyCleanupTaskIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyCleanup_WhenTimeoutExpires_AssemblyCleanupTaskIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyCleanup_WhenTimeoutExpires_AssemblyCleanupTaskIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyCleanup_WhenTimeoutExpires_AssemblyCleanupTaskIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyCleanup_WhenTimeoutExpires_FromRunSettings_AssemblyCleanupIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyCleanup_WhenTimeoutExpires_FromRunSettings_AssemblyCleanupIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyCleanup_WhenTimeoutExpires_FromRunSettings_AssemblyCleanupIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyCleanup_WhenTimeoutExpires_FromRunSettings_AssemblyCleanupIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInit_WhenTestContextCancelled_AssemblyInitializeTaskIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInit_WhenTestContextCancelled_AssemblyInitializeTaskIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInit_WhenTestContextCancelled_AssemblyInitializeTaskIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInit_WhenTestContextCancelled_AssemblyInitializeTaskIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInit_WhenTimeoutExpires_AssemblyInitializeTaskIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInit_WhenTimeoutExpires_AssemblyInitializeTaskIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInit_WhenTimeoutExpires_AssemblyInitializeTaskIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInit_WhenTimeoutExpires_AssemblyInitializeTaskIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInit_WhenTimeoutExpiresAndTestContextTokenIsUsed_AssemblyInitializeExits(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInit_WhenTimeoutExpiresAndTestContextTokenIsUsed_AssemblyInitializeExits(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInit_WhenTimeoutExpiresAndTestContextTokenIsUsed_AssemblyInitializeExits(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInit_WhenTimeoutExpiresAndTestContextTokenIsUsed_AssemblyInitializeExits(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInitialize_WhenTimeoutExpires_AssemblyInitializeIsCancelled_AttributeTakesPrecedence(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInitialize_WhenTimeoutExpires_AssemblyInitializeIsCancelled_AttributeTakesPrecedence(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInitialize_WhenTimeoutExpires_AssemblyInitializeIsCancelled_AttributeTakesPrecedence(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInitialize_WhenTimeoutExpires_AssemblyInitializeIsCancelled_AttributeTakesPrecedence(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInitialize_WhenTimeoutExpires_FromRunSettings_AssemblyInitializeIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInitialize_WhenTimeoutExpires_FromRunSettings_AssemblyInitializeIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInitialize_WhenTimeoutExpires_FromRunSettings_AssemblyInitializeIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.AssemblyInitialize_WhenTimeoutExpires_FromRunSettings_AssemblyInitializeIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassCleanup_WhenTimeoutExpires_ClassCleanupIsCancelled_AttributeTakesPrecedence(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassCleanup_WhenTimeoutExpires_ClassCleanupIsCancelled_AttributeTakesPrecedence(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassCleanup_WhenTimeoutExpires_ClassCleanupIsCancelled_AttributeTakesPrecedence(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassCleanup_WhenTimeoutExpires_ClassCleanupIsCancelled_AttributeTakesPrecedence(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassCleanup_WhenTimeoutExpires_FromRunSettings_ClassCleanupIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassCleanup_WhenTimeoutExpires_FromRunSettings_ClassCleanupIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassCleanup_WhenTimeoutExpires_FromRunSettings_ClassCleanupIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassCleanup_WhenTimeoutExpires_FromRunSettings_ClassCleanupIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassInitialize_WhenTimeoutExpires_ClassInitializeIsCancelled_AttributeTakesPrecedence(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassInitialize_WhenTimeoutExpires_ClassInitializeIsCancelled_AttributeTakesPrecedence(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassInitialize_WhenTimeoutExpires_ClassInitializeIsCancelled_AttributeTakesPrecedence(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassInitialize_WhenTimeoutExpires_ClassInitializeIsCancelled_AttributeTakesPrecedence(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassInitialize_WhenTimeoutExpires_FromRunSettings_ClassInitializeIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassInitialize_WhenTimeoutExpires_FromRunSettings_ClassInitializeIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassInitialize_WhenTimeoutExpires_FromRunSettings_ClassInitializeIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.BaseClassInitialize_WhenTimeoutExpires_FromRunSettings_ClassInitializeIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanup_WhenTimeoutExpires_ClassCleanupIsCancelled_AttributeTakesPrecedence(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanup_WhenTimeoutExpires_ClassCleanupIsCancelled_AttributeTakesPrecedence(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanup_WhenTimeoutExpires_ClassCleanupIsCancelled_AttributeTakesPrecedence(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanup_WhenTimeoutExpires_ClassCleanupIsCancelled_AttributeTakesPrecedence(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanup_WhenTimeoutExpires_ClassCleanupTaskIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanup_WhenTimeoutExpires_ClassCleanupTaskIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanup_WhenTimeoutExpires_ClassCleanupTaskIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanup_WhenTimeoutExpires_ClassCleanupTaskIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanup_WhenTimeoutExpires_FromRunSettings_ClassCleanupIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanup_WhenTimeoutExpires_FromRunSettings_ClassCleanupIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanup_WhenTimeoutExpires_FromRunSettings_ClassCleanupIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanup_WhenTimeoutExpires_FromRunSettings_ClassCleanupIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanupBase_WhenTimeoutExpires_ClassCleanupTaskIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanupBase_WhenTimeoutExpires_ClassCleanupTaskIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanupBase_WhenTimeoutExpires_ClassCleanupTaskIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassCleanupBase_WhenTimeoutExpires_ClassCleanupTaskIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInit_WhenTestContextCancelled_ClassInitializeTaskIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInit_WhenTestContextCancelled_ClassInitializeTaskIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInit_WhenTestContextCancelled_ClassInitializeTaskIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInit_WhenTestContextCancelled_ClassInitializeTaskIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInit_WhenTimeoutExpires_ClassInitializeTaskIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInit_WhenTimeoutExpires_ClassInitializeTaskIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInit_WhenTimeoutExpires_ClassInitializeTaskIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInit_WhenTimeoutExpires_ClassInitializeTaskIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInit_WhenTimeoutExpiresAndTestContextTokenIsUsed_ClassInitializeExits(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInit_WhenTimeoutExpiresAndTestContextTokenIsUsed_ClassInitializeExits(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInit_WhenTimeoutExpiresAndTestContextTokenIsUsed_ClassInitializeExits(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInit_WhenTimeoutExpiresAndTestContextTokenIsUsed_ClassInitializeExits(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitBase_WhenTestContextCancelled_ClassInitializeTaskIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitBase_WhenTestContextCancelled_ClassInitializeTaskIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitBase_WhenTestContextCancelled_ClassInitializeTaskIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitBase_WhenTestContextCancelled_ClassInitializeTaskIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitBase_WhenTimeoutExpires_ClassInitializeTaskIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitBase_WhenTimeoutExpires_ClassInitializeTaskIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitBase_WhenTimeoutExpires_ClassInitializeTaskIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitBase_WhenTimeoutExpires_ClassInitializeTaskIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitBase_WhenTimeoutExpiresAndTestContextTokenIsUsed_ClassInitializeExits(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitBase_WhenTimeoutExpiresAndTestContextTokenIsUsed_ClassInitializeExits(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitBase_WhenTimeoutExpiresAndTestContextTokenIsUsed_ClassInitializeExits(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitBase_WhenTimeoutExpiresAndTestContextTokenIsUsed_ClassInitializeExits(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitialize_WhenTimeoutExpires_ClassInitializeIsCancelled_AttributeTakesPrecedence(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitialize_WhenTimeoutExpires_ClassInitializeIsCancelled_AttributeTakesPrecedence(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitialize_WhenTimeoutExpires_ClassInitializeIsCancelled_AttributeTakesPrecedence(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitialize_WhenTimeoutExpires_ClassInitializeIsCancelled_AttributeTakesPrecedence(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitialize_WhenTimeoutExpires_FromRunSettings_ClassInitializeIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitialize_WhenTimeoutExpires_FromRunSettings_ClassInitializeIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitialize_WhenTimeoutExpires_FromRunSettings_ClassInitializeIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.ClassInitialize_WhenTimeoutExpires_FromRunSettings_ClassInitializeIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestCleanup_WhenTimeoutExpires_FromRunSettings_TestCleanupIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestCleanup_WhenTimeoutExpires_FromRunSettings_TestCleanupIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestCleanup_WhenTimeoutExpires_FromRunSettings_TestCleanupIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestCleanup_WhenTimeoutExpires_FromRunSettings_TestCleanupIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestCleanup_WhenTimeoutExpires_TestCleanupIsCancelled_AttributeTakesPrecedence(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestCleanup_WhenTimeoutExpires_TestCleanupIsCancelled_AttributeTakesPrecedence(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestCleanup_WhenTimeoutExpires_TestCleanupIsCancelled_AttributeTakesPrecedence(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestCleanup_WhenTimeoutExpires_TestCleanupIsCancelled_AttributeTakesPrecedence(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestCleanup_WhenTimeoutExpires_TestCleanupTaskIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestCleanup_WhenTimeoutExpires_TestCleanupTaskIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestCleanup_WhenTimeoutExpires_TestCleanupTaskIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestCleanup_WhenTimeoutExpires_TestCleanupTaskIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestInitialize_WhenTimeoutExpires_FromRunSettings_TestInitializeIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestInitialize_WhenTimeoutExpires_FromRunSettings_TestInitializeIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestInitialize_WhenTimeoutExpires_FromRunSettings_TestInitializeIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestInitialize_WhenTimeoutExpires_FromRunSettings_TestInitializeIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestInitialize_WhenTimeoutExpires_TestInitializeIsCancelled_AttributeTakesPrecedence(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestInitialize_WhenTimeoutExpires_TestInitializeIsCancelled_AttributeTakesPrecedence(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestInitialize_WhenTimeoutExpires_TestInitializeIsCancelled_AttributeTakesPrecedence(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestInitialize_WhenTimeoutExpires_TestInitializeIsCancelled_AttributeTakesPrecedence(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestInitialize_WhenTimeoutExpires_TestInitializeTaskIsCancelled(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestInitialize_WhenTimeoutExpires_TestInitializeTaskIsCancelled(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestInitialize_WhenTimeoutExpires_TestInitializeTaskIsCancelled(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.InitializeAndCleanupTimeout.TestInitialize_WhenTimeoutExpires_TestInitializeTaskIsCancelled(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.Messages.V100.ServerModeTests.DiscoverAndRun(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.Messages.V100.ServerModeTests.DiscoverAndRun(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.Messages.V100.ServerModeTests.DiscoverAndRun(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.Messages.V100.ServerModeTests.DiscoverAndRun(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.Messages.V100.ServerModeTests.WhenClientDies_Server_ShouldClose_Gracefully(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.Messages.V100.ServerModeTests.WhenClientDies_Server_ShouldClose_Gracefully(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.Messages.V100.ServerModeTests.WhenClientDies_Server_ShouldClose_Gracefully(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.Messages.V100.ServerModeTests.WhenClientDies_Server_ShouldClose_Gracefully(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.EnableAspireProperty_WhenUsingRunner_AllowsToRunAspireTests() -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.EnableAspireProperty_WhenUsingVSTest_AllowsToRunAspireTests() -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.EnablePlaywrightProperty_WhenUsingRunner_AllowsToRunPlaywrightTests(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.EnablePlaywrightProperty_WhenUsingRunner_AllowsToRunPlaywrightTests(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.EnablePlaywrightProperty_WhenUsingRunner_AllowsToRunPlaywrightTests(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.EnablePlaywrightProperty_WhenUsingRunner_AllowsToRunPlaywrightTests(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.EnablePlaywrightProperty_WhenUsingVSTest_AllowsToRunPlaywrightTests(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.EnablePlaywrightProperty_WhenUsingVSTest_AllowsToRunPlaywrightTests(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.EnablePlaywrightProperty_WhenUsingVSTest_AllowsToRunPlaywrightTests(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.EnablePlaywrightProperty_WhenUsingVSTest_AllowsToRunPlaywrightTests(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.Invalid_TestingProfile_Name_Should_Fail(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (multitfm,Debug) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.Invalid_TestingProfile_Name_Should_Fail(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (multitfm,Release) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.NativeAot_Smoke_Test_On_Windows() -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_CentralPackageManagement_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (multitfm,Debug) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_CentralPackageManagement_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (multitfm,Release) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_DotnetTest(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (multitfm,Debug) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_DotnetTest(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (multitfm,Release) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (multitfm,Debug) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (multitfm,Release) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Enable_Default_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool) (disabled,Debug,CodeCoverage) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Enable_Default_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool) (disabled,Release,CodeCoverage) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Enable_Default_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool) (enabled,Debug,CodeCoverage) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Enable_Default_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool) (enabled,Release,CodeCoverage) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_EnableAll_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (multitfm,Debug) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_EnableAll_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (multitfm,Release) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string, string) (multitfm,Debug,CodeCoverage) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string, string) (multitfm,Debug,CrashDump) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string, string) (multitfm,Debug,HangDump) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string, string) (multitfm,Debug,Retry) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string, string) (multitfm,Debug,TrxReport) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string, string) (multitfm,Release,CodeCoverage) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string, string) (multitfm,Release,CrashDump) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string, string) (multitfm,Release,HangDump) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string, string) (multitfm,Release,Retry) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Extensions(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string, string) (multitfm,Release,TrxReport) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_VSTest(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (multitfm,Debug) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.SdkTests.RunTests_With_VSTest(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (multitfm,Release) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.TestDiscoveryTests.DiscoverTests_FindsAllTests(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.TestDiscoveryTests.DiscoverTests_FindsAllTests(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.TestDiscoveryTests.DiscoverTests_FindsAllTests(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.TestDiscoveryTests.DiscoverTests_FindsAllTests(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.TestDiscoveryTests.DiscoverTests_WithFilter_FindsOnlyFilteredOnes(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.TestDiscoveryTests.DiscoverTests_WithFilter_FindsOnlyFilteredOnes(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.TestDiscoveryTests.DiscoverTests_WithFilter_FindsOnlyFilteredOnes(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.TestDiscoveryTests.DiscoverTests_WithFilter_FindsOnlyFilteredOnes(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenChangedInAssemblyInitialize_IsPassedToTestMethod(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenChangedInAssemblyInitialize_IsPassedToTestMethod(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenChangedInAssemblyInitialize_IsPassedToTestMethod(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenChangedInAssemblyInitialize_IsPassedToTestMethod(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenChangedInClassInitialize_IsPassedToTestMethod(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenChangedInClassInitialize_IsPassedToTestMethod(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenChangedInClassInitialize_IsPassedToTestMethod(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenChangedInClassInitialize_IsPassedToTestMethod(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenChangedInTestInitialize_IsPassedToTestMethod(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenChangedInTestInitialize_IsPassedToTestMethod(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenChangedInTestInitialize_IsPassedToTestMethod(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenChangedInTestInitialize_IsPassedToTestMethod(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenCultureIsNotSet_TestMethodFails(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenCultureIsNotSet_TestMethodFails(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenCultureIsNotSet_TestMethodFails(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadContextTests.ThreadingContext_WhenCultureIsNotSet_TestMethodFails(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.LifecycleAttributesTaskThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.LifecycleAttributesTaskThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.LifecycleAttributesTaskThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.LifecycleAttributesTaskThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.LifecycleAttributesValueTaskThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.LifecycleAttributesValueTaskThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.LifecycleAttributesValueTaskThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.LifecycleAttributesVoidThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.LifecycleAttributesVoidThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.LifecycleAttributesVoidThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.LifecycleAttributesVoidThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_MainIsSTAThread_OnWindows_NoRunsettingsProvided_ThreadIsSTA(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_MainIsSTAThread_OnWindows_NoRunsettingsProvided_ThreadIsSTA(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_MainIsSTAThread_OnWindows_NoRunsettingsProvided_ThreadIsSTA(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_MainIsSTAThread_OnWindows_NoRunsettingsProvided_ThreadIsSTA(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksForMTA_ThreadIsMTA(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksForMTA_ThreadIsMTA(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksForMTA_ThreadIsMTA(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksForMTA_ThreadIsMTA(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksForSTA_ThreadIsSTA(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksForSTA_ThreadIsSTA(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksForSTA_ThreadIsSTA(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksForSTA_ThreadIsSTA(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_NoRunsettingsProvided_ThreadIsNotSTA(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_NoRunsettingsProvided_ThreadIsNotSTA(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_NoRunsettingsProvided_ThreadIsNotSTA(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_NoRunsettingsProvided_ThreadIsNotSTA(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForMTA_ThreadIsNotSTA(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForMTA_ThreadIsNotSTA(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForMTA_ThreadIsNotSTA(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForMTA_ThreadIsNotSTA(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnNonWindows_ThreadIsNotSTAAndWarningIsEmitted(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnNonWindows_ThreadIsNotSTAAndWarningIsEmitted(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnNonWindows_ThreadIsNotSTAAndWarningIsEmitted(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnNonWindows_ThreadIsNotSTAAndWarningIsEmitted(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net462) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net6.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net7.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ThreadingTests.TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string) (net8.0) -MSTest.Acceptance.IntegrationTests.MSTest.Acceptance.IntegrationTests.ValueTaskTests.CanUseValueTaskForAllKnownLocations() diff --git a/test/IntegrationTests/MSTest.IntegrationTests/ClsTests.cs b/test/IntegrationTests/MSTest.IntegrationTests/ClsTests.cs index 919aff6141..042cf09853 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/ClsTests.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/ClsTests.cs @@ -25,8 +25,8 @@ public void TestsAreRun() testResults, "TestMethod", "IntDataRow (10)", - "StringDataRow (some string)", - "StringDataRow2 (some string)", - "StringDataRow2 (some other string)"); + "StringDataRow (\"some string\")", + "StringDataRow2 (\"some string\")", + "StringDataRow2 (\"some other string\")"); } } diff --git a/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs b/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs index acc04e2ce3..e36a2f78ef 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs @@ -81,17 +81,17 @@ public void ExecuteCustomTestExtensibilityWithTestDataTests() // Assert VerifyE2E.TestsPassed( testResults, - "CustomTestMethod2 (B)", - "CustomTestMethod2 (B)", - "CustomTestMethod2 (B)"); + "CustomTestMethod2 (\"B\")", + "CustomTestMethod2 (\"B\")", + "CustomTestMethod2 (\"B\")"); VerifyE2E.TestsFailed( testResults, - "CustomTestMethod2 (A)", - "CustomTestMethod2 (A)", - "CustomTestMethod2 (A)", - "CustomTestMethod2 (C)", - "CustomTestMethod2 (C)", - "CustomTestMethod2 (C)"); + "CustomTestMethod2 (\"A\")", + "CustomTestMethod2 (\"A\")", + "CustomTestMethod2 (\"A\")", + "CustomTestMethod2 (\"C\")", + "CustomTestMethod2 (\"C\")", + "CustomTestMethod2 (\"C\")"); } } diff --git a/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DataRowTests.cs b/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DataRowTests.cs index 115a02c03b..ab245fcaa6 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DataRowTests.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DataRowTests.cs @@ -21,11 +21,11 @@ public void ExecuteOnlyDerivedClassDataRowsWhenBothBaseAndDerivedClassHasDataRow // Assert VerifyE2E.TestsPassed( testResults, - "DataRowTestMethod (BaseString1)", - "DataRowTestMethod (BaseString2)", - "DataRowTestMethod (BaseString3)", - "DataRowTestMethod (DerivedString1)", - "DataRowTestMethod (DerivedString2)"); + "DataRowTestMethod (\"BaseString1\")", + "DataRowTestMethod (\"BaseString2\")", + "DataRowTestMethod (\"BaseString3\")", + "DataRowTestMethod (\"DerivedString1\")", + "DataRowTestMethod (\"DerivedString2\")"); } public void ExecuteOnlyDerivedClassDataRowsWhenItOverridesBaseClassDataRows_SimpleDataRows() @@ -40,8 +40,8 @@ public void ExecuteOnlyDerivedClassDataRowsWhenItOverridesBaseClassDataRows_Simp // Assert VerifyE2E.TestsPassed( testResults, - "DataRowTestMethod (DerivedString1)", - "DataRowTestMethod (DerivedString2)"); + "DataRowTestMethod (\"DerivedString1\")", + "DataRowTestMethod (\"DerivedString2\")"); } public void DataRowsExecuteWithRequiredAndOptionalParameters() @@ -57,8 +57,8 @@ public void DataRowsExecuteWithRequiredAndOptionalParameters() VerifyE2E.TestsPassed( testResults, "DataRowTestMethodWithSomeOptionalParameters (123)", - "DataRowTestMethodWithSomeOptionalParameters (123,DerivedOptionalString1)", - "DataRowTestMethodWithSomeOptionalParameters (123,DerivedOptionalString2,DerivedOptionalString3)"); + "DataRowTestMethodWithSomeOptionalParameters (123,\"DerivedOptionalString1\")", + "DataRowTestMethodWithSomeOptionalParameters (123,\"DerivedOptionalString2\",\"DerivedOptionalString3\")"); } public void DataRowsExecuteWithParamsArrayParameter() @@ -74,9 +74,9 @@ public void DataRowsExecuteWithParamsArrayParameter() VerifyE2E.TestsPassed( testResults, "DataRowTestMethodWithParamsParameters (2)", - "DataRowTestMethodWithParamsParameters (2,DerivedSingleParamsArg)", - "DataRowTestMethodWithParamsParameters (2,DerivedParamsArg1,DerivedParamsArg2)", - "DataRowTestMethodWithParamsParameters (2,DerivedParamsArg1,DerivedParamsArg2,DerivedParamsArg3)"); + "DataRowTestMethodWithParamsParameters (2,\"DerivedSingleParamsArg\")", + "DataRowTestMethodWithParamsParameters (2,\"DerivedParamsArg1\",\"DerivedParamsArg2\")", + "DataRowTestMethodWithParamsParameters (2,\"DerivedParamsArg1\",\"DerivedParamsArg2\",\"DerivedParamsArg3\")"); } public void DataRowsFailWhenInvalidArgumentsProvided() @@ -93,7 +93,7 @@ public void DataRowsFailWhenInvalidArgumentsProvided() testResults, "DataRowTestMethodFailsWithInvalidArguments ()", "DataRowTestMethodFailsWithInvalidArguments (2)", - "DataRowTestMethodFailsWithInvalidArguments (2,DerivedRequiredArgument,DerivedOptionalArgument,DerivedExtraArgument)"); + "DataRowTestMethodFailsWithInvalidArguments (2,\"DerivedRequiredArgument\",\"DerivedOptionalArgument\",\"DerivedExtraArgument\")"); } public void DataRowsShouldSerializeDoublesProperly() @@ -124,7 +124,7 @@ public void DataRowsShouldSerializeMixedTypesProperly() // Assert VerifyE2E.TestsPassed( testResults, - "DataRowTestMixed (10,10,10,10,10,10,10,10)"); + "DataRowTestMixed (10,10,10,10,10,10,10,\"10\")"); } public void DataRowsShouldSerializeEnumsProperly() @@ -139,7 +139,7 @@ public void DataRowsShouldSerializeEnumsProperly() // Assert VerifyE2E.TestsPassed( testResults, - "DataRowEnums ()", + "DataRowEnums (null)", "DataRowEnums (Alfa)", "DataRowEnums (Beta)", "DataRowEnums (Gamma)"); @@ -202,35 +202,35 @@ public void ExecuteDataRowTests_Enums() "DataRowEnum_ULong (Alfa)", "DataRowEnum_ULong (Beta)", "DataRowEnum_ULong (Gamma)", - "DataRowEnums_Nullable_SByte ()", + "DataRowEnums_Nullable_SByte (null)", "DataRowEnums_Nullable_SByte (Alfa)", "DataRowEnums_Nullable_SByte (Beta)", "DataRowEnums_Nullable_SByte (Gamma)", - "DataRowEnums_Nullable_Byte ()", + "DataRowEnums_Nullable_Byte (null)", "DataRowEnums_Nullable_Byte (Alfa)", "DataRowEnums_Nullable_Byte (Beta)", "DataRowEnums_Nullable_Byte (Gamma)", - "DataRowEnums_Nullable_Short ()", + "DataRowEnums_Nullable_Short (null)", "DataRowEnums_Nullable_Short (Alfa)", "DataRowEnums_Nullable_Short (Beta)", "DataRowEnums_Nullable_Short (Gamma)", - "DataRowEnums_Nullable_UShort ()", + "DataRowEnums_Nullable_UShort (null)", "DataRowEnums_Nullable_UShort (Alfa)", "DataRowEnums_Nullable_UShort (Beta)", "DataRowEnums_Nullable_UShort (Gamma)", - "DataRowEnums_Nullable_Int ()", + "DataRowEnums_Nullable_Int (null)", "DataRowEnums_Nullable_Int (Alfa)", "DataRowEnums_Nullable_Int (Beta)", "DataRowEnums_Nullable_Int (Gamma)", - "DataRowEnums_Nullable_UInt ()", + "DataRowEnums_Nullable_UInt (null)", "DataRowEnums_Nullable_UInt (Alfa)", "DataRowEnums_Nullable_UInt (Beta)", "DataRowEnums_Nullable_UInt (Gamma)", - "DataRowEnums_Nullable_Long ()", + "DataRowEnums_Nullable_Long (null)", "DataRowEnums_Nullable_Long (Alfa)", "DataRowEnums_Nullable_Long (Beta)", "DataRowEnums_Nullable_Long (Gamma)", - "DataRowEnums_Nullable_ULong ()", + "DataRowEnums_Nullable_ULong (null)", "DataRowEnums_Nullable_ULong (Alfa)", "DataRowEnums_Nullable_ULong (Beta)", "DataRowEnums_Nullable_ULong (Gamma)", @@ -275,43 +275,43 @@ public void ExecuteDataRowTests_Regular() "DataRow1 (20)", "DataRow1 (30)", "DataRow1 (40)", - "DataRow2 (10,String parameter,True,False)", - "DataRow2 (20,String parameter,True,False)", - "DataRow2 (30,String parameter,True,False)", - "DataRow2 (40,String parameter,True,False)", + "DataRow2 (10,\"String parameter\",True,False)", + "DataRow2 (20,\"String parameter\",True,False)", + "DataRow2 (30,\"String parameter\",True,False)", + "DataRow2 (40,\"String parameter\",True,False)", "DataRowTestDouble (10.01,20.01)", "DataRowTestDouble (10.02,20.02)", - "DataRowTestMixed (1,10,10,10,10,10,10,10,10)", - "DataRowTestMixed (2,10,10,10,10,10,10,10,10)", - "DataRowTestMixed (3,10,10,10,10,10,10,10,10)", - "DataRowTestMixed (4,10,10,10,10,10,10,10,10)", - "NullValueInData (john.doe@example.com,abc123,)", - "NullValueInData (john.doe@example.com,abc123,/unit/test)", - "NullValue ()", - "OneStringArray (System.String[])", - "TwoStringArrays (System.String[],System.String[])", - "OneObjectArray (System.Object[])", - "TwoObjectArrays (System.Object[],System.Object[])", - "ThreeObjectArrays (System.Object[],System.Object[],System.Object[])", - "FourObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[])", - "FiveObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "SixObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "SevenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "EightObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "NineObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "TenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "ElevenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "TwelveObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "ThirteenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "FourteenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "FifteenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "SixteenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", + "DataRowTestMixed (1,10,10,10,10,10,10,10,\"10\")", + "DataRowTestMixed (2,10,10,10,10,10,10,10,\"10\")", + "DataRowTestMixed (3,10,10,10,10,10,10,10,\"10\")", + "DataRowTestMixed (4,10,10,10,10,10,10,10,\"10\")", + "NullValueInData (\"john.doe@example.com\",\"abc123\",null)", + "NullValueInData (\"john.doe@example.com\",\"abc123\",\"/unit/test\")", + "NullValue (null)", + "OneStringArray ([\"\"])", + "TwoStringArrays ([\"\"],[\"1.4\",\"message\"])", + "OneObjectArray ([\"\",1])", + "TwoObjectArrays ([\"\",1],[3])", + "ThreeObjectArrays ([1],[2],[3])", + "FourObjectArrays ([1],[2],[3],[4])", + "FiveObjectArrays ([1],[2],[3],[4],[5])", + "SixObjectArrays ([1],[2],[3],[4],[5],[6])", + "SevenObjectArrays ([1],[2],[3],[4],[5],[6],[7])", + "EightObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8])", + "NineObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9])", + "TenObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10])", + "ElevenObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11])", + "TwelveObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])", + "ThirteenObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13])", + "FourteenObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14])", + "FifteenObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15])", + "SixteenObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16])", "MultipleIntegersWrappedWithParams (1,2,3,4,5)"); VerifyE2E.TestsFailed( testResults, "DataRowTestMethodFailsWithInvalidArguments ()", "DataRowTestMethodFailsWithInvalidArguments (2)", - "DataRowTestMethodFailsWithInvalidArguments (2,DerivedRequiredArgument,DerivedOptionalArgument,DerivedExtraArgument)"); + "DataRowTestMethodFailsWithInvalidArguments (2,\"DerivedRequiredArgument\",\"DerivedOptionalArgument\",\"DerivedExtraArgument\")"); } } diff --git a/test/IntegrationTests/MSTest.IntegrationTests/TestId.DefaultStrategy.cs b/test/IntegrationTests/MSTest.IntegrationTests/TestId.DefaultStrategy.cs index a1b16197f0..a43cc293ed 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/TestId.DefaultStrategy.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/TestId.DefaultStrategy.cs @@ -24,9 +24,9 @@ public void TestIdUniqueness_DataRowArray_DefaultStrategy() VerifyE2E.FailedTestCount(testResults, 0); VerifyE2E.TestsPassed( testResults, - "DataRowArraysTests (0,System.Int32[])", - "DataRowArraysTests (0,System.Int32[])", - "DataRowArraysTests (0,System.Int32[])"); + "DataRowArraysTests (0,[])", + "DataRowArraysTests (0,[0])", + "DataRowArraysTests (0,[0,0,0])"); // We cannot assert the expected ID as it is path dependent testResults.Select(x => x.TestCase.Id.ToString()).Should().OnlyHaveUniqueItems(); @@ -45,10 +45,10 @@ public void TestIdUniqueness_DataRowString_DefaultStrategy() VerifyE2E.FailedTestCount(testResults, 0); VerifyE2E.TestsPassed( testResults, - "DataRowStringTests ()", - "DataRowStringTests ()", - "DataRowStringTests ( )", - "DataRowStringTests ( )"); + "DataRowStringTests (null)", + "DataRowStringTests (\"\")", + "DataRowStringTests (\" \")", + "DataRowStringTests (\" \")"); // We cannot assert the expected ID as it is path dependent testResults.Select(x => x.TestCase.Id.ToString()).Should().OnlyHaveUniqueItems(); diff --git a/test/IntegrationTests/MSTest.IntegrationTests/TestId.FullyQualifiedStrategy.cs b/test/IntegrationTests/MSTest.IntegrationTests/TestId.FullyQualifiedStrategy.cs index 00677eb872..14e9c68444 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/TestId.FullyQualifiedStrategy.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/TestId.FullyQualifiedStrategy.cs @@ -24,9 +24,9 @@ public void TestIdUniqueness_DataRowArray_FullyQualifiedStrategy() VerifyE2E.FailedTestCount(testResults, 0); VerifyE2E.TestsPassed( testResults, - "DataRowArraysTests (0,System.Int32[])", - "DataRowArraysTests (0,System.Int32[])", - "DataRowArraysTests (0,System.Int32[])"); + "DataRowArraysTests (0,[])", + "DataRowArraysTests (0,[0])", + "DataRowArraysTests (0,[0,0,0])"); // We cannot assert the expected ID as it is path dependent testResults.Select(x => x.TestCase.Id.ToString()).Should().OnlyHaveUniqueItems(); @@ -45,10 +45,10 @@ public void TestIdUniqueness_DataRowString_FullyQualifiedStrategy() VerifyE2E.FailedTestCount(testResults, 0); VerifyE2E.TestsPassed( testResults, - "DataRowStringTests ()", - "DataRowStringTests ()", - "DataRowStringTests ( )", - "DataRowStringTests ( )"); + "DataRowStringTests (null)", + "DataRowStringTests (\"\")", + "DataRowStringTests (\" \")", + "DataRowStringTests (\" \")"); // We cannot assert the expected ID as it is path dependent testResults.Select(x => x.TestCase.Id.ToString()).Should().OnlyHaveUniqueItems(); diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DeploymentTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DeploymentTests.cs index f0a90b5911..0125ea0206 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DeploymentTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DeploymentTests.cs @@ -12,11 +12,13 @@ public class DeploymentTests : CLITestBase private const string TestAssetNever = "DeploymentTestProject.Never"; private const string TestAssetPreserveNewest = "DeploymentTestProject.PreserveNewest"; private const string RunSetting = - @" - - false - - "; + """ + + + false + + + """; public void ValidateTestSourceDependencyDeployment_net462() => ValidateTestSourceDependencyDeployment("net462"); diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DesktopCSharpCLITests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DesktopCSharpCLITests.cs index 9444d0f31b..f6dc092dbb 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DesktopCSharpCLITests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DesktopCSharpCLITests.cs @@ -12,11 +12,13 @@ public class DesktopCSharpCLITests : CLITestBase private const string X86ReleaseTestProject = "DesktopTestProjectx86Release"; private const string X64ReleaseTestProject = "DesktopTestProjectx64Release"; private const string RunSetting = - @" - - x64 - - "; + """ + + + x64 + + + """; public void DiscoverTestsx86Debug() { @@ -69,7 +71,7 @@ public void RunAllTestsx64Release() private void DoDiscoveryAndValidateDiscoveredTests(string[] sources, string runSettings = "") { InvokeVsTestForDiscovery(sources, runSettings); - string[] listOfTests = new string[] { "SampleUnitTestProject.UnitTest1.PassingTest", "SampleUnitTestProject.UnitTest1.FailingTest", "SampleUnitTestProject.UnitTest1.SkippingTest" }; + string[] listOfTests = ["SampleUnitTestProject.UnitTest1.PassingTest", "SampleUnitTestProject.UnitTest1.FailingTest", "SampleUnitTestProject.UnitTest1.SkippingTest"]; ValidateDiscoveredTests(listOfTests); } diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/FixturesTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/FixturesTests.cs new file mode 100644 index 0000000000..56a788a403 --- /dev/null +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/FixturesTests.cs @@ -0,0 +1,158 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.MSTestV2.CLIAutomation; + +namespace MSTest.VstestConsoleWrapper.IntegrationTests; + +public class FixturesTests : CLITestBase +{ + private const string AssetName = "FixturesTestProject"; + + private static readonly string AssemblyInitialize = "FixturesTestProject1.UnitTest1.AssemblyInitialize(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext)"; + private static readonly string AssemblyCleanup = "FixturesTestProject1.UnitTest1.AssemblyCleanup"; + private static readonly string ClassInitialize = "FixturesTestProject1.UnitTest1.ClassInitialize(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext)"; + private static readonly string ClassCleanup = "FixturesTestProject1.UnitTest1.ClassCleanup"; + private static readonly string TestMethod = "FixturesTestProject1.UnitTest1.Test"; + private static readonly string PassingTest = "FixturesTestProject1.UnitTest1.PassingTest"; + + private readonly string[] _tests = + [ + AssemblyInitialize, + AssemblyCleanup, + ClassInitialize, + ClassCleanup, + TestMethod, + PassingTest + ]; + + public void FixturesDisabled_DoesNotReport_FixtureTests() + { + string runSettings = GetRunSettings(false, true, true, true, true, true); + + // Discover tests, + InvokeVsTestForDiscovery([AssetName], runSettings); + ValidateDiscoveredTests([TestMethod, PassingTest]); + + // Tests, + InvokeVsTestForExecution([AssetName], runSettings); + ValidatePassedTests([TestMethod, PassingTest]); + } + + public void FixturesEnabled_DoesReport_FixtureTests() + { + string runSettings = GetRunSettings(true, true, true, true, true, true); + + // Discover tests, + InvokeVsTestForDiscovery([AssetName], runSettings); + + ValidateDiscoveredTests(_tests); + + // Run tests, + InvokeVsTestForExecution([AssetName], runSettings); + + ValidatePassedTests(_tests); + } + + public void AssemblyInitialize_Fails_TestMethod_Class_Skipped() + { + string runSettings = GetRunSettings(true, false, true, true, true, true); + + InvokeVsTestForExecution([AssetName], runSettings); + + ValidatePassedTests([AssemblyCleanup]); + ValidateFailedTests(false, [AssemblyInitialize, TestMethod, PassingTest]); + ValidateSkippedTests([ClassInitialize, ClassCleanup]); + } + + public void AssemblyCleanup_OnlyFails_AssemblyCleanup() + { + string runSettings = GetRunSettings(true, true, false, true, true, true); + + InvokeVsTestForExecution([AssetName], runSettings); + ValidatePassedTests([AssemblyInitialize, ClassInitialize, ClassCleanup, PassingTest]); + // TestMethod fails because AssemblyCleanup is executed after it and hence it fails. + ValidateFailedTests(false, [AssemblyCleanup, TestMethod]); + } + + public void ClassInitialize_OnlyFails_ClassInitialize() + { + string runSettings = GetRunSettings(true, true, true, false, true, true); + + InvokeVsTestForExecution([AssetName], runSettings); + ValidateFailedTests(false, [ClassInitialize, TestMethod, PassingTest]); + ValidatePassedTests([AssemblyInitialize, AssemblyCleanup, ClassCleanup]); + } + + public void ClassCleanup_OnlyFails_ClassCleanup() + { + string runSettings = GetRunSettings(true, true, true, true, false, true); + + InvokeVsTestForExecution([AssetName], runSettings); + ValidatePassedTests([AssemblyInitialize, AssemblyCleanup, ClassInitialize, PassingTest]); + // TestMethod fails because ClassCleanup is executed after it and hence it fails. + ValidateFailedTests(false, [ClassCleanup, TestMethod]); + } + + public void RunOnlyFixtures_DoesNot_Run_Fixtures() + { + string runSettings = GetRunSettings(true, true, true, true, true, true); + + InvokeVsTestForExecution([AssetName], runSettings, testCaseFilter: nameof(AssemblyInitialize)); + ValidateSkippedTests(AssemblyInitialize); + InvokeVsTestForExecution([AssetName], runSettings, testCaseFilter: nameof(AssemblyCleanup)); + ValidateSkippedTests(AssemblyCleanup); + InvokeVsTestForExecution([AssetName], runSettings, testCaseFilter: nameof(ClassInitialize)); + ValidateSkippedTests(ClassInitialize); + InvokeVsTestForExecution([AssetName], runSettings, testCaseFilter: nameof(ClassCleanup)); + ValidateSkippedTests(ClassCleanup); + InvokeVsTestForExecution([AssetName], runSettings, testCaseFilter: "ClassCleanup|AssemblyCleanup"); + ValidateSkippedTests([AssemblyCleanup, ClassCleanup]); + } + + public void RunSingleTest_Runs_Assembly_And_Class_Fixtures() + { + string runSettings = GetRunSettings(true, true, true, true, true, true); + + InvokeVsTestForExecution([AssetName], runSettings, testCaseFilter: nameof(PassingTest)); + ValidatePassedTests([AssemblyInitialize, AssemblyCleanup, ClassInitialize, ClassCleanup, PassingTest]); + } + + public void RunSingleTest_AssemblyInitialize_Failure_Skips_ClassFixtures() + { + string runSettings = GetRunSettings(true, false, true, true, true, true); + + InvokeVsTestForExecution([AssetName], runSettings, testCaseFilter: nameof(PassingTest)); + ValidateFailedTests(false, [AssemblyInitialize, PassingTest]); + ValidatePassedTests([AssemblyCleanup]); + // Class fixtures are not executed if AssemblyInitialize fails. + ValidateSkippedTests([ClassInitialize, ClassCleanup]); + } + + public void RunSingleTest_ClassInitialize_Failure_Runs_AssemblyFixtures() + { + string runSettings = GetRunSettings(true, true, true, false, true, true); + + InvokeVsTestForExecution([AssetName], runSettings, testCaseFilter: nameof(PassingTest)); + ValidateFailedTests(false, [ClassInitialize, PassingTest]); + ValidatePassedTests([AssemblyInitialize, AssemblyCleanup, ClassCleanup]); + } + + private string GetRunSettings(bool fixturesEnabled, bool assemblyInitialize, bool assemblyCleanup, bool classInitialize, bool classCleanup, bool test) + => $@" + + + + {assemblyInitialize} + {assemblyCleanup} + {classInitialize} + {classCleanup} + {test} + + + + {fixturesEnabled} + + +"; +} diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/ParallelExecutionTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/ParallelExecutionTests.cs index 6be866d8fc..6c9b65ae82 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/ParallelExecutionTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/ParallelExecutionTests.cs @@ -58,14 +58,16 @@ public void AllClassesShouldRunInParallel() public void NothingShouldRunInParallel() { const string RunSetting = - @" - - - 4 - ClassLevel - - - "; + """ + + + + 4 + ClassLevel + + + + """; InvokeVsTestForExecution([DoNotParallelizeTestAssetName], RunSetting); diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs index 5b172e3160..4d25cc9a15 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs @@ -13,6 +13,7 @@ public void ExecuteTestDataSourceExtensibilityTests() { InvokeVsTestForExecution([TestAssetName]); ValidatePassedTestsContain("CustomTestDataSourceTestMethod1 (1,2,3)", "CustomTestDataSourceTestMethod1 (4,5,6)"); + ValidateFailedTestsContain(false, "FxExtensibilityTestProject.TestDataSourceExTests.CustomTestDataSourceTestMethod1"); } public void ExecuteDynamicDataExtensibilityTests() @@ -29,6 +30,15 @@ public void ExecuteDynamicDataExtensibilityTests() "DynamicDataTestMethod5 (string,4,True)", "DynamicDataTestMethod6 (string,2,True)", "DynamicDataTestMethod6 (string,4,True)"); + + ValidateFailedTestsContain( + false, + "FxExtensibilityTestProject.DynamicDataExTests.DynamicEmptyDataTestMethod1", + "FxExtensibilityTestProject.DynamicDataExTests.DynamicEmptyDataTestMethod2", + "FxExtensibilityTestProject.DynamicDataExTests.DynamicEmptyDataTestMethod3", + "FxExtensibilityTestProject.DynamicDataExMoreTests.DynamicEmptyDataTestMethod4", + "FxExtensibilityTestProject.DynamicDataExMoreTests.DynamicEmptyDataTestMethod5", + "FxExtensibilityTestProject.DynamicDataExMoreTests.DynamicEmptyDataTestMethod6"); } public void ExecuteCustomTestExtensibilityTests() @@ -55,17 +65,17 @@ public void ExecuteCustomTestExtensibilityWithTestDataTests() InvokeVsTestForExecution([TestAssetName], testCaseFilter: "FullyQualifiedName~CustomTestExTests.CustomTestMethod2"); ValidatePassedTests( - "CustomTestMethod2 (B)", - "CustomTestMethod2 (B)", - "CustomTestMethod2 (B)"); + "CustomTestMethod2 (\"B\")", + "CustomTestMethod2 (\"B\")", + "CustomTestMethod2 (\"B\")"); ValidateFailedTestsCount(6); ValidateFailedTestsContain( true, - "CustomTestMethod2 (A)", - "CustomTestMethod2 (A)", - "CustomTestMethod2 (A)", - "CustomTestMethod2 (C)", - "CustomTestMethod2 (C)", - "CustomTestMethod2 (C)"); + "CustomTestMethod2 (\"A\")", + "CustomTestMethod2 (\"A\")", + "CustomTestMethod2 (\"A\")", + "CustomTestMethod2 (\"C\")", + "CustomTestMethod2 (\"C\")", + "CustomTestMethod2 (\"C\")"); } } diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataRowTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataRowTests.cs index 9b30f204b5..a5c7d9c0c1 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataRowTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataRowTests.cs @@ -14,11 +14,11 @@ public void ExecuteOnlyDerivedClassDataRowsWhenBothBaseAndDerivedClassHasDataRow InvokeVsTestForExecution([TestAssetName], testCaseFilter: "TestCategory~DataRowSimple"); ValidatePassedTestsContain( - "DataRowTestMethod (BaseString1)", - "DataRowTestMethod (BaseString2)", - "DataRowTestMethod (BaseString3)", - "DataRowTestMethod (DerivedString1)", - "DataRowTestMethod (DerivedString2)"); + "DataRowTestMethod (\"BaseString1\")", + "DataRowTestMethod (\"BaseString2\")", + "DataRowTestMethod (\"BaseString3\")", + "DataRowTestMethod (\"DerivedString1\")", + "DataRowTestMethod (\"DerivedString2\")"); // 3 tests of BaseClass.DataRowTestMethod - 3 data row results and no parent result // 2 tests of DerivedClass.DataRowTestMethod - 2 data row results and no parent result @@ -41,8 +41,8 @@ public void ExecuteOnlyDerivedClassDataRowsWhenItOverridesBaseClassDataRows_Simp InvokeVsTestForExecution([TestAssetName], testCaseFilter: "FullyQualifiedName~DerivedClass&TestCategory~DataRowSimple"); ValidatePassedTestsContain( - "DataRowTestMethod (DerivedString1)", - "DataRowTestMethod (DerivedString2)"); + "DataRowTestMethod (\"DerivedString1\")", + "DataRowTestMethod (\"DerivedString2\")"); // 2 tests of DerivedClass.DataRowTestMethod - 2 datarow result and no parent result ValidatePassedTestsCount(2); @@ -54,8 +54,8 @@ public void DataRowsExecuteWithRequiredAndOptionalParameters() ValidatePassedTestsContain( "DataRowTestMethodWithSomeOptionalParameters (123)", - "DataRowTestMethodWithSomeOptionalParameters (123,DerivedOptionalString1)", - "DataRowTestMethodWithSomeOptionalParameters (123,DerivedOptionalString2,DerivedOptionalString3)"); + "DataRowTestMethodWithSomeOptionalParameters (123,\"DerivedOptionalString1\")", + "DataRowTestMethodWithSomeOptionalParameters (123,\"DerivedOptionalString2\",\"DerivedOptionalString3\")"); // 3 tests of DerivedClass.DataRowTestMethodWithSomeOptionalParameters - 3 datarow result and no parent result ValidatePassedTestsCount(3); @@ -68,8 +68,8 @@ public void DataRowsExecuteWithAllOptionalParameters() ValidatePassedTestsContain( "DataRowTestMethodWithAllOptionalParameters ()", "DataRowTestMethodWithAllOptionalParameters (123)", - "DataRowTestMethodWithAllOptionalParameters (123,DerivedOptionalString4)", - "DataRowTestMethodWithAllOptionalParameters (123,DerivedOptionalString5,DerivedOptionalString6)"); + "DataRowTestMethodWithAllOptionalParameters (123,\"DerivedOptionalString4\")", + "DataRowTestMethodWithAllOptionalParameters (123,\"DerivedOptionalString5\",\"DerivedOptionalString6\")"); // 4 tests of DerivedClass.DataRowTestMethodWithAllOptionalParameters - 4 datarow result and no parent result ValidatePassedTestsCount(4); @@ -81,9 +81,9 @@ public void DataRowsExecuteWithParamsArrayParameter() ValidatePassedTestsContain( "DataRowTestMethodWithParamsParameters (2)", - "DataRowTestMethodWithParamsParameters (2,DerivedSingleParamsArg)", - "DataRowTestMethodWithParamsParameters (2,DerivedParamsArg1,DerivedParamsArg2)", - "DataRowTestMethodWithParamsParameters (2,DerivedParamsArg1,DerivedParamsArg2,DerivedParamsArg3)"); + "DataRowTestMethodWithParamsParameters (2,\"DerivedSingleParamsArg\")", + "DataRowTestMethodWithParamsParameters (2,\"DerivedParamsArg1\",\"DerivedParamsArg2\")", + "DataRowTestMethodWithParamsParameters (2,\"DerivedParamsArg1\",\"DerivedParamsArg2\",\"DerivedParamsArg3\")"); // 4 tests of DerivedClass.DataRowTestMethodWithParamsParameters - 4 datarow result and no parent result ValidatePassedTestsCount(4); @@ -97,7 +97,7 @@ public void DataRowsFailWhenInvalidArgumentsProvided() false, "DataRowTestMethodFailsWithInvalidArguments ()", "DataRowTestMethodFailsWithInvalidArguments (2)", - "DataRowTestMethodFailsWithInvalidArguments (2,DerivedRequiredArgument,DerivedOptionalArgument,DerivedExtraArgument)"); + "DataRowTestMethodFailsWithInvalidArguments (2,\"DerivedRequiredArgument\",\"DerivedOptionalArgument\",\"DerivedExtraArgument\")"); // 3 tests of DerivedClass.DataRowTestMethodFailsWithInvalidArguments - 3 datarow result and no parent result ValidateFailedTestsCount(3); @@ -136,35 +136,35 @@ public void ExecuteDataRowTests_Enums() "DataRowEnum_ULong (Alfa)", "DataRowEnum_ULong (Beta)", "DataRowEnum_ULong (Gamma)", - "DataRowEnums_Nullable_SByte ()", + "DataRowEnums_Nullable_SByte (null)", "DataRowEnums_Nullable_SByte (Alfa)", "DataRowEnums_Nullable_SByte (Beta)", "DataRowEnums_Nullable_SByte (Gamma)", - "DataRowEnums_Nullable_Byte ()", + "DataRowEnums_Nullable_Byte (null)", "DataRowEnums_Nullable_Byte (Alfa)", "DataRowEnums_Nullable_Byte (Beta)", "DataRowEnums_Nullable_Byte (Gamma)", - "DataRowEnums_Nullable_Short ()", + "DataRowEnums_Nullable_Short (null)", "DataRowEnums_Nullable_Short (Alfa)", "DataRowEnums_Nullable_Short (Beta)", "DataRowEnums_Nullable_Short (Gamma)", - "DataRowEnums_Nullable_UShort ()", + "DataRowEnums_Nullable_UShort (null)", "DataRowEnums_Nullable_UShort (Alfa)", "DataRowEnums_Nullable_UShort (Beta)", "DataRowEnums_Nullable_UShort (Gamma)", - "DataRowEnums_Nullable_Int ()", + "DataRowEnums_Nullable_Int (null)", "DataRowEnums_Nullable_Int (Alfa)", "DataRowEnums_Nullable_Int (Beta)", "DataRowEnums_Nullable_Int (Gamma)", - "DataRowEnums_Nullable_UInt ()", + "DataRowEnums_Nullable_UInt (null)", "DataRowEnums_Nullable_UInt (Alfa)", "DataRowEnums_Nullable_UInt (Beta)", "DataRowEnums_Nullable_UInt (Gamma)", - "DataRowEnums_Nullable_Long ()", + "DataRowEnums_Nullable_Long (null)", "DataRowEnums_Nullable_Long (Alfa)", "DataRowEnums_Nullable_Long (Beta)", "DataRowEnums_Nullable_Long (Gamma)", - "DataRowEnums_Nullable_ULong ()", + "DataRowEnums_Nullable_ULong (null)", "DataRowEnums_Nullable_ULong (Alfa)", "DataRowEnums_Nullable_ULong (Beta)", "DataRowEnums_Nullable_ULong (Gamma)", @@ -204,43 +204,43 @@ public void ExecuteRegular_DataRowTests() "DataRow1 (20)", "DataRow1 (30)", "DataRow1 (40)", - "DataRow2 (10,String parameter,True,False)", - "DataRow2 (20,String parameter,True,False)", - "DataRow2 (30,String parameter,True,False)", - "DataRow2 (40,String parameter,True,False)", + "DataRow2 (10,\"String parameter\",True,False)", + "DataRow2 (20,\"String parameter\",True,False)", + "DataRow2 (30,\"String parameter\",True,False)", + "DataRow2 (40,\"String parameter\",True,False)", "DataRowTestDouble (10.01,20.01)", "DataRowTestDouble (10.02,20.02)", - "DataRowTestMixed (1,10,10,10,10,10,10,10,10)", - "DataRowTestMixed (2,10,10,10,10,10,10,10,10)", - "DataRowTestMixed (3,10,10,10,10,10,10,10,10)", - "DataRowTestMixed (4,10,10,10,10,10,10,10,10)", - "NullValueInData (john.doe@example.com,abc123,)", - "NullValueInData (john.doe@example.com,abc123,/unit/test)", - "NullValue ()", - "OneStringArray (System.String[])", - "TwoStringArrays (System.String[],System.String[])", - "OneObjectArray (System.Object[])", - "TwoObjectArrays (System.Object[],System.Object[])", - "ThreeObjectArrays (System.Object[],System.Object[],System.Object[])", - "FourObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[])", - "FiveObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "SixObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "SevenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "EightObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "NineObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "TenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "ElevenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "TwelveObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "ThirteenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "FourteenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "FifteenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", - "SixteenObjectArrays (System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[],System.Object[])", + "DataRowTestMixed (1,10,10,10,10,10,10,10,\"10\")", + "DataRowTestMixed (2,10,10,10,10,10,10,10,\"10\")", + "DataRowTestMixed (3,10,10,10,10,10,10,10,\"10\")", + "DataRowTestMixed (4,10,10,10,10,10,10,10,\"10\")", + "NullValueInData (\"john.doe@example.com\",\"abc123\",null)", + "NullValueInData (\"john.doe@example.com\",\"abc123\",\"/unit/test\")", + "NullValue (null)", + "OneStringArray ([\"\"])", + "TwoStringArrays ([\"\"],[\"1.4\",\"message\"])", + "OneObjectArray ([\"\",1])", + "TwoObjectArrays ([\"\",1],[3])", + "ThreeObjectArrays ([1],[2],[3])", + "FourObjectArrays ([1],[2],[3],[4])", + "FiveObjectArrays ([1],[2],[3],[4],[5])", + "SixObjectArrays ([1],[2],[3],[4],[5],[6])", + "SevenObjectArrays ([1],[2],[3],[4],[5],[6],[7])", + "EightObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8])", + "NineObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9])", + "TenObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10])", + "ElevenObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11])", + "TwelveObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])", + "ThirteenObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13])", + "FourteenObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14])", + "FifteenObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15])", + "SixteenObjectArrays ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16])", "MultipleIntegersWrappedWithParams (1,2,3,4,5)"); ValidateFailedTests( false, "DataRowTestMethodFailsWithInvalidArguments ()", "DataRowTestMethodFailsWithInvalidArguments (2)", - "DataRowTestMethodFailsWithInvalidArguments (2,DerivedRequiredArgument,DerivedOptionalArgument,DerivedExtraArgument)"); + "DataRowTestMethodFailsWithInvalidArguments (2,\"DerivedRequiredArgument\",\"DerivedOptionalArgument\",\"DerivedExtraArgument\")"); } } diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/SuiteLifeCycleTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/SuiteLifeCycleTests.cs index e5b0c7e6ff..572f8cbfe0 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/SuiteLifeCycleTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/SuiteLifeCycleTests.cs @@ -1632,11 +1632,11 @@ LifeCycleClassCleanupEndOfClassAndNone.TestCleanup was called {GenerateTraceDebugPrefixedMessage("AssemblyCleanup was called")} """ - .Split(new[] { "\r\n" }, StringSplitOptions.None); + .Split(["\r\n"], StringSplitOptions.None); caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod .Messages[1].Text .Substring(expectedStart.Length) - .Split(new[] { "\r\n" }, StringSplitOptions.None) + .Split(["\r\n"], StringSplitOptions.None) .Should().BeEquivalentTo(expectedRemainingMessages); expectedStart = @@ -1682,11 +1682,11 @@ LifeCycleClassCleanupEndOfAssembly.ClassCleanup was called AssemblyCleanup was called """ - .Split(new[] { "\r\n" }, StringSplitOptions.None); + .Split(["\r\n"], StringSplitOptions.None); caseDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClassParentTestMethod .Messages[2].Text .Substring(expectedStart.Length) - .Split(new[] { "\r\n" }, StringSplitOptions.None) + .Split(["\r\n"], StringSplitOptions.None) .Should().BeEquivalentTo(expectedRemainingMessages); } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashDumpTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashDumpTests.cs new file mode 100644 index 0000000000..bb1215be87 --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashDumpTests.cs @@ -0,0 +1,159 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public sealed class CrashDumpTests : AcceptanceTestBase +{ + internal static Func RetryPolicy + => ex => ex.ToString().Contains("FAILED No such process") + || ex.ToString().Contains("FAILED 13 (Permission denied)") + || ex.ToString().Contains("Problem suspending threads"); + + private readonly TestAssetFixture _testAssetFixture; + + public CrashDumpTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) + : base(testExecutionContext) + { + _testAssetFixture = testAssetFixture; + } + + [ArgumentsProvider(nameof(TargetFrameworks.Net), typeof(TargetFrameworks))] + public async Task CrashDump_DefaultSetting_CreateDump(string tfm) + => await RetryHelper.RetryAsync( + async () => + { + string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "CrashDump", tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --results-directory {resultDirectory}"); + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); + string? dumpFile = Directory.GetFiles(resultDirectory, "CrashDump.dll_*.dmp", SearchOption.AllDirectories).SingleOrDefault(); + Assert.IsTrue(dumpFile is not null, $"Dump file not found '{tfm}'\n{testHostResult}'"); + }, 3, TimeSpan.FromSeconds(3), RetryPolicy); + + public async Task CrashDump_CustomDumpName_CreateDump() + { + string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "CrashDump", TargetFrameworks.NetCurrent.Arguments); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --crashdump-filename customdumpname.dmp --results-directory {resultDirectory}"); + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); + Assert.IsTrue(Directory.GetFiles(resultDirectory, "customdumpname.dmp", SearchOption.AllDirectories).SingleOrDefault() is not null, "Dump file not found"); + } + + [Arguments("Mini")] + [Arguments("Heap")] + [Arguments("Triage")] + [Arguments("Full")] + public async Task CrashDump_Formats_CreateDump(string format) + => await RetryHelper.RetryAsync( + async () => + { + string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "CrashDump", TargetFrameworks.NetCurrent.Arguments); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --crashdump-type {format} --results-directory {resultDirectory}"); + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); + string? dumpFile = Directory.GetFiles(resultDirectory, "CrashDump.dll_*.dmp", SearchOption.AllDirectories).SingleOrDefault(); + Assert.IsTrue(dumpFile is not null, $"Dump file not found '{format}'\n{testHostResult}'"); + File.Delete(dumpFile); + }, 3, TimeSpan.FromSeconds(3), RetryPolicy); + + public async Task CrashDump_InvalidFormat_ShouldFail() + { + string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "CrashDump", TargetFrameworks.NetCurrent.Arguments); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --crashdump-type invalid --results-directory {resultDirectory}"); + testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); + testHostResult.AssertOutputContains("Option '--crashdump-type' has invalid arguments: 'invalid' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full'"); + } + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + private const string AssetName = "CrashDumpFixture"; + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (AssetName, AssetName, + Sources + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + } + + public string TargetAssetPath => GetAssetPath(AssetName); + + private const string Sources = """ +#file CrashDump.csproj + + + + $TargetFrameworks$ + Exe + true + enable + preview + + + + + + +#file Program.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Globalization; + +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Extensions; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.Services; + +public class Startup +{ + public static async Task Main(string[] args) + { + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + builder.AddCrashDumpProvider(); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); + } +} + +public class DummyTestAdapter : ITestFramework +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public Task ExecuteRequestAsync(ExecuteRequestContext context) + { + Environment.FailFast("CrashDump"); + context.Complete(); + return Task.CompletedTask; + } +} +"""; + } +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashPlusHangDumpTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashPlusHangDumpTests.cs new file mode 100644 index 0000000000..309df2c0dd --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashPlusHangDumpTests.cs @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public sealed class CrashPlusHangDumpTests : AcceptanceTestBase +{ + private readonly TestAssetFixture _testAssetFixture; + + public CrashPlusHangDumpTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) + : base(testExecutionContext) + { + _testAssetFixture = testAssetFixture; + } + + public async Task CrashPlusHangDump_InCaseOfCrash_CreateCrashDump() + => await RetryHelper.RetryAsync( + async () => + { + string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent.Arguments); + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "CrashPlusHangDump", TargetFrameworks.NetCurrent.Arguments); + TestHostResult testHostResult = await testHost.ExecuteAsync( + $"--hangdump --hangdump-timeout 5m --crashdump --results-directory {resultDirectory}", + new Dictionary() + { + { "SLEEPTIMEMS1", "4000" }, + { "SLEEPTIMEMS2", "600000" }, + { "SHOULDCRASH", "true" }, + }); + + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); + testHostResult.AssertOutputMatchesRegex(@"Test host process with PID \'.+\' crashed, a dump file was generated"); + testHostResult.AssertOutputDoesNotContain(@"Hang dump timeout '00:00:08' expired"); + + Assert.IsTrue(Directory.GetFiles(resultDirectory, "CrashPlusHangDump.dll*_crash.dmp", SearchOption.AllDirectories).Length > 0, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); + Assert.IsFalse(Directory.GetFiles(resultDirectory, "CrashPlusHangDump*_hang.dmp", SearchOption.AllDirectories).Length > 0, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); + }, 3, TimeSpan.FromSeconds(3), CrashDumpTests.RetryPolicy); + + public async Task CrashPlusHangDump_InCaseOfHang_CreateHangDump() + => await RetryHelper.RetryAsync( + async () => + { + string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent.Arguments); + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "CrashPlusHangDump", TargetFrameworks.NetCurrent.Arguments); + TestHostResult testHostResult = await testHost.ExecuteAsync( + $"--hangdump --hangdump-timeout 8s --crashdump --results-directory {resultDirectory}", + new Dictionary() + { + { "SLEEPTIMEMS1", "4000" }, + { "SLEEPTIMEMS2", "600000" }, + { "SHOULDCRASH", "false" }, + }); + + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); + testHostResult.AssertOutputDoesNotMatchRegex(@"Test host process with PID '.+' crashed, a dump file was generated"); + testHostResult.AssertOutputContains(@"Hang dump timeout of '00:00:08' expired"); + + Assert.IsFalse(Directory.GetFiles(resultDirectory, "CrashPlusHangDump.dll*_crash.dmp", SearchOption.AllDirectories).Length > 0, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); + Assert.IsTrue(Directory.GetFiles(resultDirectory, "CrashPlusHangDump*_hang.dmp", SearchOption.AllDirectories).Length > 0, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); + }, 3, TimeSpan.FromSeconds(3), CrashDumpTests.RetryPolicy); + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + private const string AssetName = "TestAssetFixture"; + + public string TargetAssetPath => GetAssetPath(AssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (AssetName, AssetName, + Sources + .PatchTargetFrameworks(TargetFrameworks.NetCurrent) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + } + + private const string Sources = """ +#file CrashPlusHangDump.csproj + + + + $TargetFrameworks$ + Exe + true + enable + preview + + + + + + + +#file Program.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Globalization; + +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Extensions; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.Services; + +public class Startup +{ + public static async Task Main(string[] args) + { + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + builder.AddCrashDumpProvider(); + builder.AddHangDumpProvider(); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); + } +} + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage) }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + string shouldCrash = Environment.GetEnvironmentVariable("SHOULDCRASH")!; + + if (shouldCrash == "true") + { + Environment.FailFast("CrashPlusHangDump"); + } + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new PassedTestNodeStateProperty()), + })); + + Thread.Sleep(int.Parse(Environment.GetEnvironmentVariable("SLEEPTIMEMS1")!, CultureInfo.InvariantCulture)); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test2", + DisplayName = "Test2", + Properties = new PropertyBag(new PassedTestNodeStateProperty()), + })); + + Thread.Sleep(int.Parse(Environment.GetEnvironmentVariable("SLEEPTIMEMS2")!, CultureInfo.InvariantCulture)); + context.Complete(); + } +} +"""; + } +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CustomBannerTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CustomBannerTests.cs new file mode 100644 index 0000000000..a4104dcd99 --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CustomBannerTests.cs @@ -0,0 +1,173 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public class CustomBannerTests : AcceptanceTestBase +{ + private const string AssetName = "CustomBannerTest"; + private readonly TestAssetFixture _testAssetFixture; + + public CustomBannerTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) + : base(testExecutionContext) + { + _testAssetFixture = testAssetFixture; + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task UsingNoBanner_TheBannerDoesNotAppear(string tfm) + { + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--no-banner"); + + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); + testHostResult.AssertOutputDoesNotContain(TestAssetFixture.CustomBannerPrefix); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task UsingNoBanner_InTheEnvironmentVars_TheBannerDoesNotAppear(string tfm) + { + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + null, + new Dictionary() + { + { "TESTINGPLATFORM_NOBANNER", "true" }, + }); + + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); + testHostResult.AssertOutputDoesNotContain(TestAssetFixture.CustomBannerPrefix); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear(string tfm) + { + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + null, + new Dictionary() + { + { "DOTNET_NOLOGO", "true" }, + }); + + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); + testHostResult.AssertOutputDoesNotContain(TestAssetFixture.CustomBannerPrefix); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task WithoutUsingNoBanner_TheBannerAppears(string tfm) + { + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(); + + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); + testHostResult.AssertOutputMatchesRegex($"{TestAssetFixture.CustomBannerPrefix} Platform info: Name: .NET Testing Platform, Version: .+?, Hash: .*?, Date: .+?"); + } + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + public const string CustomBannerPrefix = "Custom banner |"; + + private const string CustomBannerTestCode = $$""" +#file CustomBannerTest.csproj + + + $TargetFrameworks$ + enable + enable + Exe + true + preview + + + + + + + +#file Program.cs +using System.Text; + +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Services; + +public class Program +{ + public static async Task Main(string[] args) + { + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework( + sp => new TestFrameworkCapabilities(new DummyBannerMessageOwnerCapability(sp)), + (_,__) => new DummyTestAdapter()); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); + } +} + +#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. +internal sealed class DummyBannerMessageOwnerCapability : IBannerMessageOwnerCapability +{ + private readonly IServiceProvider _serviceProvider; + + public DummyBannerMessageOwnerCapability(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public Task GetBannerMessageAsync() + { + var platformInformation = _serviceProvider.GetRequiredService(); + StringBuilder sb = new(); + sb.Append("{{CustomBannerPrefix}} Platform info: "); + sb.Append($"Name: {platformInformation.Name}"); + sb.Append($", Version: {platformInformation.Version}"); + sb.Append($", Hash: {platformInformation.CommitHash}"); + sb.Append($", Date: {platformInformation.BuildDate}"); + + return Task.FromResult(sb.ToString()); + } +} +#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 class DummyTestAdapter : ITestFramework +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + public Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + return Task.CompletedTask; + } +} +"""; + + public string TargetAssetPath => GetAssetPath(AssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (AssetName, AssetName, + CustomBannerTestCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + } + } +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs index c9a5ec4be8..5de07cfd1e 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs @@ -240,9 +240,11 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test preview + - - + + + @@ -279,7 +281,7 @@ public void TestMethod1() TestCode .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); } } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs index 2b9f113607..d863795970 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs @@ -151,9 +151,11 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test preview + - - + + + @@ -280,13 +282,13 @@ internal class Capabilities : ITestFrameworkCapabilities TestCode .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); yield return (AssetName2, AssetName2, TestCode2 .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); } } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs new file mode 100644 index 0000000000..6e63c7048c --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs @@ -0,0 +1,197 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public sealed class HangDumpTests : AcceptanceTestBase +{ + private readonly TestAssetFixture _testAssetFixture; + + public HangDumpTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) + : base(testExecutionContext) + { + _testAssetFixture = testAssetFixture; + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task HangDump_DefaultSetting_CreateDump(string tfm) + => await RetryHelper.RetryAsync( + async () => + { + string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + $"--hangdump --hangdump-timeout 8s --results-directory {resultDirectory}", + new Dictionary() + { + { "SLEEPTIMEMS1", "4000" }, + { "SLEEPTIMEMS2", "600000" }, + }); + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); + string? dumpFile = Directory.GetFiles(resultDirectory, "HangDump*.dmp", SearchOption.AllDirectories).SingleOrDefault(); + Assert.IsTrue(dumpFile is not null, $"Dump file not found '{tfm}'\n{testHostResult}'"); + }, 3, TimeSpan.FromSeconds(3), CrashDumpTests.RetryPolicy); + + public async Task HangDump_CustomFileName_CreateDump() + { + string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent.Arguments); + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent.Arguments); + TestHostResult testHostResult = await testHost.ExecuteAsync( + $"--hangdump --hangdump-timeout 8s --hangdump-filename myhungdumpfile_%p.dmp --results-directory {resultDirectory}", + new Dictionary() + { + { "SLEEPTIMEMS1", "4000" }, + { "SLEEPTIMEMS2", "600000" }, + }); + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); + string? dumpFile = Directory.GetFiles(resultDirectory, "myhungdumpfile_*.dmp", SearchOption.AllDirectories).SingleOrDefault(); + Assert.IsTrue(dumpFile is not null, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); + } + + [Arguments("Mini")] + [Arguments("Heap")] + [Arguments("Triage")] + [Arguments("Full")] + public async Task HangDump_Formats_CreateDump(string format) + => await RetryHelper.RetryAsync( + async () => + { + string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), format); + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent.Arguments); + TestHostResult testHostResult = await testHost.ExecuteAsync( + $"--hangdump --hangdump-timeout 8s --hangdump-type {format} --results-directory {resultDirectory}", + new Dictionary() + { + { "SLEEPTIMEMS1", "4000" }, + { "SLEEPTIMEMS2", "600000" }, + }); + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); + string? dumpFile = Directory.GetFiles(resultDirectory, "HangDump*.dmp", SearchOption.AllDirectories).SingleOrDefault(); + Assert.IsTrue(dumpFile is not null, $"Dump file not found '{format}'\n{testHostResult}'"); + }, 3, TimeSpan.FromSeconds(3), CrashDumpTests.RetryPolicy); + + public async Task HangDump_InvalidFormat_ShouldFail() + { + string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent.Arguments); + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent.Arguments); + TestHostResult testHostResult = await testHost.ExecuteAsync( + $"--hangdump --hangdump-timeout 8s --hangdump-type invalid --results-directory {resultDirectory}", + new Dictionary() + { + { "SLEEPTIMEMS1", "4000" }, + { "SLEEPTIMEMS2", "600000" }, + }); + testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); + testHostResult.AssertOutputContains("Option '--hangdump-type' has invalid arguments: 'invalid' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) and 'Full'"); + } + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + private const string AssetName = "TestAssetFixture"; + + public string TargetAssetPath => GetAssetPath(AssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (AssetName, AssetName, + Sources + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + } + + private const string Sources = """ +#file HangDump.csproj + + + + $TargetFrameworks$ + Exe + true + enable + preview + + + + + + + +#file Program.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Globalization; + +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Extensions; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.Services; + +public class Startup +{ + public static async Task Main(string[] args) + { + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + builder.AddHangDumpProvider(); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); + } +} + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage) }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new PassedTestNodeStateProperty()), + })); + + Thread.Sleep(int.Parse(Environment.GetEnvironmentVariable("SLEEPTIMEMS1")!, CultureInfo.InvariantCulture)); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test2", + DisplayName = "Test2", + Properties = new PropertyBag(new PassedTestNodeStateProperty()), + })); + + Thread.Sleep(int.Parse(Environment.GetEnvironmentVariable("SLEEPTIMEMS2")!, CultureInfo.InvariantCulture)); + + context.Complete(); + } +} +"""; + } +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs new file mode 100644 index 0000000000..50b663070b --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs @@ -0,0 +1,588 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public class HelpInfoTests : AcceptanceTestBase +{ + private readonly TestAssetFixture _testAssetFixture; + + public HelpInfoTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) + : base(testExecutionContext) + { + _testAssetFixture = testAssetFixture; + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task Help_WhenNoExtensionRegistered_OutputDefaultHelpContent(string tfm) + { + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + + const string wildcardMatchPattern = $""" +.NET Testing Platform v* +Usage {TestAssetFixture.NoExtensionAssetName}* [option providers] [extension option providers] +Execute a .NET Test Application. +Options: + --diagnostic Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag + --diagnostic-filelogger-synchronouswrite Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). Note that this is slowing down the test execution. + --diagnostic-output-directory Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory. + --diagnostic-output-fileprefix Prefix for the log file name that will replace '[log]_.' + --diagnostic-verbosity Define the level of the verbosity for the --diagnostic. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical' + --exit-on-process-exit Exit the test process if dependent process exits. PID must be provided. + --help Show the command line help. + --ignore-exit-code Do not report non successful exit value for specific exit codes (e.g. '--ignore-exit-code 8;9' ignore exit code 8 and 9 and will return 0 in these case) + --info Display .NET test application information. + --list-tests List available tests. + --minimum-expected-tests Specifies the minimum number of tests that are expected to run. + --results-directory The directory where the test results are going to be placed. If the specified directory doesn't exist, it's created. The default is TestResults in the directory that contains the test application. +Extension options: + --treenode-filter Use a tree filter to filter down the tests to execute +"""; + + testHostResult.AssertOutputMatches(wildcardMatchPattern); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task Help_WhenNoExtensionRegisteredAndUnknownOptionIsSpecified_OutputDefaultHelpContentAndUnknownOption(string tfm) + { + const string UnknownOption = "aaa"; + + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync($"-{UnknownOption}"); + + testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); + + const string wildcardMatchPattern = $""" +.NET Testing Platform v* +Unknown option '--{UnknownOption}' +Usage {TestAssetFixture.NoExtensionAssetName}* [option providers] [extension option providers] +Execute a .NET Test Application. +Options: + --diagnostic Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag + --diagnostic-filelogger-synchronouswrite Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). Note that this is slowing down the test execution. + --diagnostic-output-directory Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory. + --diagnostic-output-fileprefix Prefix for the log file name that will replace '[log]_.' + --diagnostic-verbosity Define the level of the verbosity for the --diagnostic. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical' + --exit-on-process-exit Exit the test process if dependent process exits. PID must be provided. + --help Show the command line help. + --ignore-exit-code Do not report non successful exit value for specific exit codes (e.g. '--ignore-exit-code 8;9' ignore exit code 8 and 9 and will return 0 in these case) + --info Display .NET test application information. + --list-tests List available tests. + --minimum-expected-tests Specifies the minimum number of tests that are expected to run. + --results-directory The directory where the test results are going to be placed. If the specified directory doesn't exist, it's created. The default is TestResults in the directory that contains the test application. +Extension options: + --treenode-filter Use a tree filter to filter down the tests to execute +"""; + + testHostResult.AssertOutputMatches(wildcardMatchPattern); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task Info_WhenNoExtensionRegistered_OutputDefaultInfoContent(string tfm) + { + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--info"); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + + string regexMatchPattern = $""" +.NET Testing Platform v.+ \[.+\] +Microsoft Testing Platform: + Version: .+ + Dynamic Code Supported: True + Runtime information: .+({Environment.NewLine} Runtime location: .+)? + Test module: .+{TestAssetFixture.NoExtensionAssetName}.* +Built-in command line providers: + PlatformCommandLineProvider + Name: Platform command line provider + Version: .+ + Description: Microsoft Testing Platform command line provider + Options: + --client-host + Arity: 1 + Hidden: True + Description: Specify the hostname of the client\. + --client-port + Arity: 1 + Hidden: True + Description: Specify the port of the client\. + --diagnostic + Arity: 0 + Hidden: False + Description: Enable the diagnostic logging\. The default log level is 'Trace'\. The file will be written in the output directory with the name log_\[MMddHHssfff\]\.diag + --diagnostic-filelogger-synchronouswrite + Arity: 0 + Hidden: False + Description: Force the built-in file logger to write the log synchronously\. Useful for scenario where you don't want to lose any log \(i\.e\. in case of crash\)\. Note that this is slowing down the test execution\. + --diagnostic-output-directory + Arity: 1 + Hidden: False + Description: Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory\. + --diagnostic-output-fileprefix + Arity: 1 + Hidden: False + Description: Prefix for the log file name that will replace '\[log\]_\.' + --diagnostic-verbosity + Arity: 1 + Hidden: False + Description: Define the level of the verbosity for the --diagnostic\. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical' + --dotnet-test-pipe + Arity: 1 + Hidden: True + Description: dotnet test pipe\. + --exit-on-process-exit + Arity: 1 + Hidden: False + Description: Exit the test process if dependent process exits\. PID must be provided\. + --help + Arity: 0 + Hidden: False + Description: Show the command line help\. + --ignore-exit-code + Arity: 1 + Hidden: False + Description: Do not report non successful exit value for specific exit codes \(e\.g\. '--ignore-exit-code 8;9' ignore exit code 8 and 9 and will return 0 in these case\) + --info + Arity: 0 + Hidden: False + Description: Display \.NET test application information\. + --internal-testhostcontroller-pid + Arity: 0\.\.1 + Hidden: True + Description: Eventual parent test host controller PID\. + --internal-testingplatform-skipbuildercheck + Arity: 0 + Hidden: True + Description: For testing purposes + --internal-vstest-adapter + Arity: 0 + Hidden: True + Description: Bridge to VSTest APIs + --list-tests + Arity: 0 + Hidden: False + Description: List available tests\. + --minimum-expected-tests + Arity: 0\.\.1 + Hidden: False + Description: Specifies the minimum number of tests that are expected to run\. + --no-banner + Arity: 0\.\.1 + Hidden: True + Description: Do not display the startup banner, the copyright message or the telemetry banner\. + --port + Arity: 1 + Hidden: True + Description: Specify the port of the server\. + --results-directory + Arity: 1 + Hidden: False + Description: The directory where the test results are going to be placed\. If the specified directory doesn't exist, it's created\. The default is TestResults in the directory that contains the test application\. + --server + Arity: 0\.\.1 + Hidden: True + Description: Enable the server mode\. +Registered command line providers: + TestingFrameworkExtension + Name: Microsoft Testing Framework + Version: .+ + Description: Microsoft Testing Framework\. This framework allows you to test your code anywhere in any mode \(all OSes, all platforms, all configurations\.\.\.\)\. + Options: + --treenode-filter + Arity: 1 + Hidden: False + Description: Use a tree filter to filter down the tests to execute +Registered tools: + There are no registered tools\. +"""; + + testHostResult.AssertOutputMatchesRegex(regexMatchPattern); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task Help_WithAllExtensionsRegistered_OutputFullHelpContent(string tfm) + { + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.AllExtensionsTargetAssetPath, TestAssetFixture.AllExtensionsAssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + + string wildcardPattern = $""" +.NET Testing Platform v* +Usage {TestAssetFixture.AllExtensionsAssetName}* [option providers] [extension option providers] +Execute a .NET Test Application. +Options: + --diagnostic Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag + --diagnostic-filelogger-synchronouswrite Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). Note that this is slowing down the test execution. + --diagnostic-output-directory Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory. + --diagnostic-output-fileprefix Prefix for the log file name that will replace '[log]_.' + --diagnostic-verbosity Define the level of the verbosity for the --diagnostic. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical' + --exit-on-process-exit Exit the test process if dependent process exits. PID must be provided. + --help Show the command line help. + --ignore-exit-code Do not report non successful exit value for specific exit codes (e.g. '--ignore-exit-code 8;9' ignore exit code 8 and 9 and will return 0 in these case) + --info Display .NET test application information. + --list-tests List available tests. + --minimum-expected-tests Specifies the minimum number of tests that are expected to run. + --results-directory The directory where the test results are going to be placed. If the specified directory doesn't exist, it's created. The default is TestResults in the directory that contains the test application. + --retry-failed-tests Enable retry failed tests + --retry-failed-tests-max-percentage Disable retry mechanism if the percentage of failed tests is greater than the specified value + --retry-failed-tests-max-tests Disable retry mechanism if the number of failed tests is greater than the specified value +Extension options: + --crashdump [net6.0+ only] Generate a dump file if the test process crashes + --crashdump-filename Specify the name of the dump file + --crashdump-type Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + --hangdump Generate a dump file if the test process hangs + --hangdump-filename Specify the name of the dump file + --hangdump-timeout Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + --hangdump-type Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + --report-trx Enable generating TRX report + --report-trx-filename The name of the generated TRX report + --treenode-filter Use a tree filter to filter down the tests to execute +"""; + + testHostResult.AssertOutputMatches(wildcardPattern); + } + + [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + public async Task Info_WithAllExtensionsRegistered_OutputFullInfoContent(string tfm) + { + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.AllExtensionsTargetAssetPath, TestAssetFixture.AllExtensionsAssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--info"); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + + string wildcardPattern = $""" +.NET Testing Platform v* [*] +Microsoft Testing Platform: + Version: * + Dynamic Code Supported: True + Runtime information: *{(tfm == TargetFrameworks.NetFramework[0].Arguments ? $"{Environment.NewLine} Runtime location: *" : string.Empty)} + Test module: *{TestAssetFixture.AllExtensionsAssetName}* +Built-in command line providers: + PlatformCommandLineProvider + Name: Platform command line provider + Version: * + Description: Microsoft Testing Platform command line provider + Options: + --client-host + Arity: 1 + Hidden: True + Description: Specify the hostname of the client. + --client-port + Arity: 1 + Hidden: True + Description: Specify the port of the client. + --diagnostic + Arity: 0 + Hidden: False + Description: Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag + --diagnostic-filelogger-synchronouswrite + Arity: 0 + Hidden: False + Description: Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). Note that this is slowing down the test execution. + --diagnostic-output-directory + Arity: 1 + Hidden: False + Description: Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory. + --diagnostic-output-fileprefix + Arity: 1 + Hidden: False + Description: Prefix for the log file name that will replace '[log]_.' + --diagnostic-verbosity + Arity: 1 + Hidden: False + Description: Define the level of the verbosity for the --diagnostic. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical' + --dotnet-test-pipe + Arity: 1 + Hidden: True + Description: dotnet test pipe. + --exit-on-process-exit + Arity: 1 + Hidden: False + Description: Exit the test process if dependent process exits. PID must be provided. + --help + Arity: 0 + Hidden: False + Description: Show the command line help. + --ignore-exit-code + Arity: 1 + Hidden: False + Description: Do not report non successful exit value for specific exit codes (e.g. '--ignore-exit-code 8;9' ignore exit code 8 and 9 and will return 0 in these case) + --info + Arity: 0 + Hidden: False + Description: Display .NET test application information. + --internal-testhostcontroller-pid + Arity: 0..1 + Hidden: True + Description: Eventual parent test host controller PID. + --internal-testingplatform-skipbuildercheck + Arity: 0 + Hidden: True + Description: For testing purposes + --internal-vstest-adapter + Arity: 0 + Hidden: True + Description: Bridge to VSTest APIs + --list-tests + Arity: 0 + Hidden: False + Description: List available tests. + --minimum-expected-tests + Arity: 0..1 + Hidden: False + Description: Specifies the minimum number of tests that are expected to run. + --no-banner + Arity: 0..1 + Hidden: True + Description: Do not display the startup banner, the copyright message or the telemetry banner. + --port + Arity: 1 + Hidden: True + Description: Specify the port of the server. + --results-directory + Arity: 1 + Hidden: False + Description: The directory where the test results are going to be placed. If the specified directory doesn't exist, it's created. The default is TestResults in the directory that contains the test application. + --server + Arity: 0..1 + Hidden: True + Description: Enable the server mode. +Registered command line providers: + CrashDumpCommandLineProvider + Name: Crash dump + Version: * + Description: [net6.0+ only] Produce crash dump files when the test execution process crashes unexpectedly + Options: + --crashdump + Arity: 0 + Hidden: False + Description: [net6.0+ only] Generate a dump file if the test process crashes + --crashdump-filename + Arity: 1 + Hidden: False + Description: Specify the name of the dump file + --crashdump-type + Arity: 1 + Hidden: False + Description: Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' or 'Full'. Default type is 'Full'. For more information visit https://learn.microsoft.com/dotnet/core/diagnostics/collect-dumps-crash#types-of-mini-dumps + HangDumpCommandLineProvider + Name: Hang dump + Version: * + Description: Produce hang dump files when a test execution exceed a given time. + Options: + --hangdump + Arity: 0 + Hidden: False + Description: Generate a dump file if the test process hangs + --hangdump-filename + Arity: 1 + Hidden: False + Description: Specify the name of the dump file + --hangdump-timeout + Arity: 1 + Hidden: False + Description: Specify the timeout after which the dump will be generated. The timeout value is specified in one of the following formats: 1.5h, 1.5hour, 1.5hours, 90m, 90min, 90minute, 90minutes 5400s, 5400sec, 5400second, 5400seconds. Default is 30m. + --hangdump-type + Arity: 1 + Hidden: False + Description: Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + RetryCommandLineOptionsProvider + Name: Retry failed tests + Version: * + Description: Retry failed tests feature allows to restart test execution upon failure. + Options: + --internal-retry-pipename + Arity: 1 + Hidden: True + Description: Communication between the test host and the retry infra. + --retry-failed-tests + Arity: 1 + Hidden: False + Description: Enable retry failed tests + --retry-failed-tests-max-percentage + Arity: 1 + Hidden: False + Description: Disable retry mechanism if the percentage of failed tests is greater than the specified value + --retry-failed-tests-max-tests + Arity: 1 + Hidden: False + Description: Disable retry mechanism if the number of failed tests is greater than the specified value + TestingFrameworkExtension + Name: Microsoft Testing Framework + Version: * + Description: Microsoft Testing Framework. This framework allows you to test your code anywhere in any mode (all OSes, all platforms, all configurations...). + Options: + --treenode-filter + Arity: 1 + Hidden: False + Description: Use a tree filter to filter down the tests to execute + TrxReportGeneratorCommandLine + Name: TRX report generator + Version: * + Description: Produce a TRX report for the current test session + Options: + --report-trx + Arity: 0 + Hidden: False + Description: Enable generating TRX report + --report-trx-filename + Arity: 1 + Hidden: False + Description: The name of the generated TRX report +Registered tools: + TrxCompareTool + Command: ms-trxcompare + Name: TRX comparer tool + Version: * + Description: This tool allows to compare and highights differences between 2 TRX reports + Tool command line providers: + TrxCompareTool + Name: TRX comparer tool + Version: * + Description: This tool allows to compare and highights differences between 2 TRX reports + Options: + --baseline-trx + Arity: 1 + Hidden: False + Description: The baseline TRX file + --trx-to-compare + Arity: 1 + Hidden: False + Description: The TRX file to compare with the baseline +"""; + + testHostResult.AssertOutputMatches(wildcardPattern); + } + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + public const string AllExtensionsAssetName = "AllExtensionsInfoTest"; + public const string NoExtensionAssetName = "NoExtensionInfoTest"; + + private const string AllExtensionsTestCode = """ +#file AllExtensionsInfoTest.csproj + + + $TargetFrameworks$ + enable + enable + Exe + preview + + + + + + + + + + + + + + + +#file Program.cs +using AllExtensionsInfoTest; +ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); +builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); +builder.AddCrashDumpProvider(); +builder.AddHangDumpProvider(); +builder.AddHotReloadProvider(); +builder.AddRetryProvider(); +builder.AddTrxReportProvider(); +using ITestApplication app = await builder.BuildAsync(); +return await app.RunAsync(); + +#file UnitTest1.cs +namespace AllExtensionsInfoTest; + +[TestGroup] +public class UnitTest1 +{ + public void TestMethod1() + { + Assert.IsTrue(true); + } +} + +#file Usings.cs +global using Microsoft.Testing.Platform.Builder; +global using Microsoft.Testing.Internal.Framework; +global using Microsoft.Testing.Extensions; +"""; + + private const string NoExtensionTestCode = """ +#file NoExtensionInfoTest.csproj + + + $TargetFrameworks$ + enable + enable + Exe + true + preview + + + + + + + + + + +#file Program.cs +using NoExtensionInfoTest; +ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); +builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); +using ITestApplication app = await builder.BuildAsync(); +return await app.RunAsync(); + +#file UnitTest1.cs +namespace NoExtensionInfoTest; + +[TestGroup] +public class UnitTest1 +{ + public void TestMethod1() + { + Assert.IsTrue(true); + } +} + +#file Usings.cs +global using Microsoft.Testing.Platform.Builder; +global using Microsoft.Testing.Internal.Framework; +global using Microsoft.Testing.Extensions; +"""; + + public string NoExtensionTargetAssetPath => GetAssetPath(NoExtensionAssetName); + + public string AllExtensionsTargetAssetPath => GetAssetPath(AllExtensionsAssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (NoExtensionAssetName, NoExtensionAssetName, + NoExtensionTestCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + yield return (AllExtensionsAssetName, AllExtensionsAssetName, + AllExtensionsTestCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + } + } +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpTests.cs deleted file mode 100644 index e89d1ad7eb..0000000000 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpTests.cs +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - -namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; - -[TestGroup] -public class HelpTests : AcceptanceTestBase -{ - private readonly TestAssetFixture _testAssetFixture; - - public HelpTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) - { - _testAssetFixture = testAssetFixture; - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task Help_WhenNoExtensionRegistered_OutputDefaultHelpContent(string tfm) - { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); - - testHostResult.AssertExitCodeIs(ExitCodes.Success); - - const string RegexMatchPattern = $""" -.NET Testing Platform v.+ \[.+\] -Usage {TestAssetFixture.NoExtensionAssetName}.* \[option providers\] \[extension option providers\] -Execute a .NET Test Application\. -Options: - --diagnostic Enable the diagnostic logging\. The default log level is 'Trace'\. The file will be written in the output directory with the name log_\[MMddHHssfff\]\.diag - --diagnostic-filelogger-synchronouswrite Force the built-in file logger to write the log synchronously\. Useful for scenario where you don't want to lose any log \(i\.e\. in case of crash\)\. Note that this is slowing down the test execution\. - --diagnostic-output-directory Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory\. - --diagnostic-output-fileprefix Prefix for the log file name that will replace '\[log\]_\.' - --diagnostic-verbosity Define the level of the verbosity for the --diagnostic\. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical' - --exit-on-process-exit Exit the test process if dependent process exits\. PID must be provided. - --help Show the command line help\. - --ignore-exit-code Do not report non successful exit value for specific exit codes \(e\.g\. '--ignore-exit-code 8;9' ignore exit code 8 and 9 and will return 0 in these case\) - --info Display \.NET test application information\. - --list-tests List available tests\. - --minimum-expected-tests Specifies the minimum number of tests that are expected to run\. - --results-directory The directory where the test results are going to be placed\. If the specified directory doesn't exist, it's created\. The default is TestResults in the directory that contains the test application\. -Extension options: - --treenode-filter Use a tree filter to filter down the tests to execute -"""; - - testHostResult.AssertOutputMatchesRegex(RegexMatchPattern); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task Help_WhenNoExtensionRegisteredAndUnknownOptionIsSpecified_OutputDefaultHelpContentAndUnknownOption(string tfm) - { - const string UnknownOption = "aaa"; - - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync($"-{UnknownOption}"); - - testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); - - const string RegexMatchPattern = $""" -Unknown option '--{UnknownOption}' -Usage {TestAssetFixture.NoExtensionAssetName}.* \[option providers\] \[extension option providers\] -Execute a \.NET Test Application\. -Options: - --diagnostic Enable the diagnostic logging\. The default log level is 'Trace'\. The file will be written in the output directory with the name log_\[MMddHHssfff\]\.diag - --diagnostic-filelogger-synchronouswrite Force the built-in file logger to write the log synchronously\. Useful for scenario where you don't want to lose any log \(i\.e\. in case of crash\)\. Note that this is slowing down the test execution\. - --diagnostic-output-directory Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory\. - --diagnostic-output-fileprefix Prefix for the log file name that will replace '\[log\]_\.' - --diagnostic-verbosity Define the level of the verbosity for the --diagnostic\. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical' - --exit-on-process-exit Exit the test process if dependent process exits\. PID must be provided. - --help Show the command line help\. - --ignore-exit-code Do not report non successful exit value for specific exit codes \(e\.g\. '--ignore-exit-code 8;9' ignore exit code 8 and 9 and will return 0 in these case\) - --info Display \.NET test application information\. - --list-tests List available tests\. - --minimum-expected-tests Specifies the minimum number of tests that are expected to run\. - --results-directory The directory where the test results are going to be placed\. If the specified directory doesn't exist, it's created\. The default is TestResults in the directory that contains the test application\. -Extension options: - --treenode-filter Use a tree filter to filter down the tests to execute -"""; - - testHostResult.AssertOutputMatchesRegex(RegexMatchPattern); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task Help_WhenMSTestExtensionRegistered_OutputHelpContentOfRegisteredExtension(string tfm) - { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.MSTestTargetAssetPath, TestAssetFixture.MSTestAssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); - - testHostResult.AssertExitCodeIs(ExitCodes.Success); - - const string RegexMatchPattern = $""" -.NET Testing Platform v.+ \[.+\] -Usage {TestAssetFixture.MSTestAssetName}.* \[option providers\] \[extension option providers\] -Execute a .NET Test Application\. -Options: - --diagnostic Enable the diagnostic logging\. The default log level is 'Trace'\. The file will be written in the output directory with the name log_\[MMddHHssfff\]\.diag - --diagnostic-filelogger-synchronouswrite Force the built-in file logger to write the log synchronously\. Useful for scenario where you don't want to lose any log \(i\.e\. in case of crash\)\. Note that this is slowing down the test execution\. - --diagnostic-output-directory Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory\. - --diagnostic-output-fileprefix Prefix for the log file name that will replace '\[log\]_\.' - --diagnostic-verbosity Define the level of the verbosity for the --diagnostic\. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical' - --exit-on-process-exit Exit the test process if dependent process exits\. PID must be provided. - --help Show the command line help\. - --ignore-exit-code Do not report non successful exit value for specific exit codes \(e\.g\. '--ignore-exit-code 8;9' ignore exit code 8 and 9 and will return 0 in these case\) - --info Display \.NET test application information\. - --list-tests List available tests\. - --minimum-expected-tests Specifies the minimum number of tests that are expected to run\. - --results-directory The directory where the test results are going to be placed\. If the specified directory doesn't exist, it's created\. The default is TestResults in the directory that contains the test application\. -Extension options: - --filter Filters tests using the given expression\. For more information, see the Filter option details section\. For more information and examples on how to use selective unit test filtering, see https://learn\.microsoft\.com/dotnet/core/testing/selective-unit-tests\. - --settings The path, relative or absolute, to the \.runsettings file\.For more information and examples on how to configure test run, see https://learn\.microsoft\.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file -"""; - - testHostResult.AssertOutputMatchesRegex(RegexMatchPattern); - } - - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) - { - public const string NoExtensionAssetName = "NoExtensionHelpTest"; - public const string MSTestAssetName = "MSTestHelpTest"; - - private const string NoExtensionHelpTestCode = """ -#file NoExtensionHelpTest.csproj - - - $TargetFrameworks$ - enable - enable - Exe - true - preview - - - - - - - - -#file Program.cs -using NoExtensionHelpTest; -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); - -#file UnitTest1.cs -namespace NoExtensionHelpTest; - -[TestGroup] -public class UnitTest1 -{ - public void TestMethod1() - { - Assert.IsTrue(true); - } -} - -#file Usings.cs -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Internal.Framework; -global using Microsoft.Testing.Extensions; -"""; - - private const string MSTestCode = """ -#file MSTestHelpTest.csproj - - - $TargetFrameworks$ - enable - enable - Exe - true - preview - true - - - - - - - - - - -#file Program.cs -using MSTestHelpTest; - -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddMSTest(() => new[] { typeof(Program).Assembly }); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); - -#file UnitTest1.cs -namespace MSTestHelpTest; - -[TestClass] -public class UnitTest1 -{ - [TestMethod] - public void TestMethod1() - { - } -} - -#file Usings.cs -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Extensions; -global using Microsoft.VisualStudio.TestTools.UnitTesting; -"""; - - public string NoExtensionTargetAssetPath => GetAssetPath(NoExtensionAssetName); - - public string MSTestTargetAssetPath => GetAssetPath(MSTestAssetName); - - public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() - { - yield return (NoExtensionAssetName, NoExtensionAssetName, - NoExtensionHelpTestCode - .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion)); - yield return (MSTestAssetName, MSTestAssetName, - MSTestCode - .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); - } - } -} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs index 1884b2ba5e..f0731aa7c7 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs @@ -14,6 +14,40 @@ public static void AssertExitCodeIs(this TestHostResult testHostResult, int exit public static void AssertExitCodeIsNot(this TestHostResult testHostResult, int exitCode, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) => Assert.That(exitCode != testHostResult.ExitCode, GenerateFailedAssertionMessage(testHostResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); + public static void AssertOutputMatches(this TestHostResult testHostResult, string wildcardPattern, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) + { + string[] wildcardLines = wildcardPattern.Split(Environment.NewLine); + for (int i = 0; i < testHostResult.StandardOutputLines.Count; i++) + { + string outputLine = testHostResult.StandardOutputLines[i]; + + if (wildcardLines[i].Contains('*')) + { + string matchingPatternLine = + "^" + + Regex.Escape(wildcardLines[i]).Replace("\\*", ".*") + + "$"; + + Assert.That( + Regex.IsMatch(outputLine, matchingPatternLine, RegexOptions.Singleline), + $"Output on line {i + 1}{Environment.NewLine}{outputLine}{Environment.NewLine}doesn't match pattern{Environment.NewLine}{matchingPatternLine}", + callerMemberName: callerMemberName, + callerFilePath: callerFilePath, + callerLineNumber: callerLineNumber); + } + else + { + string expectedLine = wildcardLines[i]; + Assert.That( + string.Equals(outputLine, expectedLine, StringComparison.Ordinal), + $"Output on line {i + 1} (length: {outputLine.Length}){Environment.NewLine}{outputLine}{Environment.NewLine}doesn't match line (length: {expectedLine.Length}){Environment.NewLine}{expectedLine}", + callerMemberName: callerMemberName, + callerFilePath: callerFilePath, + callerLineNumber: callerLineNumber); + } + } + } + public static void AssertOutputMatchesRegex(this TestHostResult testHostResult, string pattern, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) => Assert.That(Regex.IsMatch(testHostResult.StandardOutput, pattern), GenerateFailedAssertionMessage(testHostResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceTestBase.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceTestBase.cs index 7d9ed84f51..89d6ad8c3b 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceTestBase.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceTestBase.cs @@ -14,11 +14,7 @@ namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; /// public abstract class AcceptanceTestBase : TestBase { - private const string MicrosoftTestingPlatformNamePrefix = "Microsoft.Testing.Platform."; private const string NuGetPackageExtensionName = ".nupkg"; -#if !MSTEST_DOWNLOADED - private const string MSTestTestFrameworkPackageNamePrefix = "MSTest.TestFramework."; -#endif protected const string CurrentMSTestSourceCode = """ #file MSTestProject.csproj @@ -38,7 +34,6 @@ public abstract class AcceptanceTestBase : TestBase - @@ -64,12 +59,12 @@ static AcceptanceTestBase() var versionsPropFileDoc = XDocument.Load(Path.Combine(RootFinder.Find(), "eng", "Versions.props")); #if MSTEST_DOWNLOADED MSTestVersion = ExtractVersionFromVersionPropsFile(versionsPropFileDoc, "MSTestVersion"); - MicrosoftTestingPlatformVersion = ExtractVersionFromPackage(Constants.ArtifactsPackagesShipping, MicrosoftTestingPlatformNamePrefix); - MicrosoftTestingPlatformExtensionsVersion = MicrosoftTestingPlatformVersion; + MicrosoftTestingPlatformVersion = ExtractVersionFromVersionPropsFile(versionsPropFileDoc, "MSTestVersion"); + MicrosoftTestingEnterpriseExtensionsVersion = ExtractVersionFromPackage(Constants.ArtifactsPackagesShipping, "Microsoft.Testing.Extensions."); #else - MSTestVersion = ExtractVersionFromPackage(Constants.ArtifactsPackagesShipping, MSTestTestFrameworkPackageNamePrefix); - MicrosoftTestingPlatformVersion = ExtractVersionFromPackage(Constants.ArtifactsTmpPackages, MicrosoftTestingPlatformNamePrefix); - MicrosoftTestingPlatformExtensionsVersion = ExtractVersionFromVersionPropsFile(versionsPropFileDoc, "MicrosoftTestingPlatformVersion"); + MSTestVersion = ExtractVersionFromPackage(Constants.ArtifactsPackagesShipping, "MSTest.TestFramework."); + MicrosoftTestingPlatformVersion = ExtractVersionFromPackage(Constants.ArtifactsPackagesShipping, "Microsoft.Testing.Platform."); + MicrosoftTestingEnterpriseExtensionsVersion = ExtractVersionFromVersionPropsFile(versionsPropFileDoc, "MicrosoftTestingInternalFrameworkVersion"); #endif } @@ -78,7 +73,7 @@ protected AcceptanceTestBase(ITestExecutionContext testExecutionContext) { } - internal static string RID { get; private set; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win-x64" : "linux-x64"; + internal static string RID { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win-x64" : "linux-x64"; public static string MSTestVersion { get; private set; } @@ -86,7 +81,7 @@ protected AcceptanceTestBase(ITestExecutionContext testExecutionContext) public static string MicrosoftTestingPlatformVersion { get; private set; } - public static string MicrosoftTestingPlatformExtensionsVersion { get; private set; } + public static string MicrosoftTestingEnterpriseExtensionsVersion { get; private set; } internal static IEnumerable> GetBuildMatrixTfmBuildVerbConfiguration() { @@ -153,18 +148,21 @@ private static string ExtractVersionFromPackage(string rootFolder, string packag // For some packages the find pattern will match multiple packages, for example: // Microsoft.Testing.Platform.1.0.0.nupkg // Microsoft.Testing.Platform.Extensions.1.0.0.nupkg - // So we need to find the first package that contains a number after the prefix. + // So we need to find a package that contains a number after the prefix. // Ideally, we would want to do a full validation to check this is a nuget version number, but that's too much work for now. - matches = - [ - matches.Select(path => (path, fileName: Path.GetFileName(path)[packagePrefixName.Length..])) - .First(tuple => int.TryParse(tuple.fileName[0].ToString(), CultureInfo.InvariantCulture, out _)).path - ]; + matches = matches + // (full path, file name without prefix) + .Select(path => (path, fileName: Path.GetFileName(path)[packagePrefixName.Length..])) + // check if first character of file name without prefix is number + .Where(tuple => int.TryParse(tuple.fileName[0].ToString(), CultureInfo.InvariantCulture, out _)) + // take the full path + .Select(tuple => tuple.path) + .ToArray(); } if (matches.Length != 1) { - throw new InvalidOperationException($"Was expecting to find a single NuGet package named '{packagePrefixName}' in '{rootFolder}' but found {matches.Length}."); + throw new InvalidOperationException($"Was expecting to find a single NuGet package named '{packagePrefixName}' in '{rootFolder}', but found {matches.Length}: '{string.Join("', '", matches.Select(m => Path.GetFileName(m)))}'."); } string packageFullName = Path.GetFileName(matches[0]); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/IgnoreExitCodeTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/IgnoreExitCodeTests.cs index ea6b31fd76..3d81507a2f 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/IgnoreExitCodeTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/IgnoreExitCodeTests.cs @@ -103,23 +103,23 @@ public IgnoreExitCodeTests(ITestExecutionContext testExecutionContext, Acceptanc public async Task If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string tfm, BuildConfiguration buildConfiguration, string commandLine, string environmentVariable) { using TestAsset generator = await TestAsset.GenerateAssetAsync( - AssetName, - SourceCode - .PatchCodeWithReplace("$TargetFramework$", tfm) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); - - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - compilationResult = await DotnetCli.RunAsync( - $"build -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -r {RID}", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - var testHost = TestInfrastructure.TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration); - TestHostResult testHostResult = await testHost.ExecuteAsync( + AssetName, + SourceCode + .PatchCodeWithReplace("$TargetFramework$", tfm) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + + string assetPath = generator.TargetAssetPath; + string globalPackagesPath = _acceptanceFixture.NuGetGlobalPackagesFolder.Path; + await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {assetPath} -r {RID}", globalPackagesPath); + await DotnetCli.RunAsync($"build -m:1 -nodeReuse:false {assetPath} -c {buildConfiguration} -r {RID}", globalPackagesPath); + var host = TestInfrastructure.TestHost.LocateFrom(assetPath, AssetName, tfm, buildConfiguration: buildConfiguration); + TestHostResult hostResult = await host.ExecuteAsync( command: commandLine, - environmentVariables: environmentVariable is null ? null : new Dictionary() + environmentVariables: new Dictionary { { EnvironmentVariableConstants.TESTINGPLATFORM_EXITCODE_IGNORE, environmentVariable }, }); - testHostResult.AssertOutputContains("Failed! - Failed: 1, Passed: 0, Skipped: 0, Total: 1"); - Assert.AreEqual(0, testHostResult.ExitCode); + hostResult.AssertOutputContains("Failed! - Failed: 1, Passed: 0, Skipped: 0, Total: 1"); + Assert.AreEqual(0, hostResult.ExitCode); } } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/InfoTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/InfoTests.cs deleted file mode 100644 index 3fe5444cc8..0000000000 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/InfoTests.cs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - -namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; - -[TestGroup] -public class InfoTests : AcceptanceTestBase -{ - private readonly TestAssetFixture _testAssetFixture; - - public InfoTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) - { - _testAssetFixture = testAssetFixture; - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task Info_WhenNoExtensionRegistered_OutputDefaultInfoContent(string tfm) - { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--info"); - - testHostResult.AssertExitCodeIs(ExitCodes.Success); - - string regexMatchPattern = $""" -.NET Testing Platform v.+ \[.+\] -Microsoft Testing Platform: - Version: .+ - Dynamic Code Supported: True - Runtime information: .+({Environment.NewLine} Runtime location: .+)? - Test module: .+{TestAssetFixture.NoExtensionAssetName}.* -Built-in command line providers: - PlatformCommandLineProvider - Name: Platform command line provider - Version: .+ - Description: Microsoft Testing Platform command line provider - Options: - --client-host - Arity: 1 - Hidden: True - Description: Specify the hostname of the client\. - --client-port - Arity: 1 - Hidden: True - Description: Specify the port of the client\. - --diagnostic - Arity: 0 - Hidden: False - Description: Enable the diagnostic logging\. The default log level is 'Trace'\. The file will be written in the output directory with the name log_\[MMddHHssfff\]\.diag - --diagnostic-filelogger-synchronouswrite - Arity: 0 - Hidden: False - Description: Force the built-in file logger to write the log synchronously\. Useful for scenario where you don't want to lose any log \(i\.e\. in case of crash\)\. Note that this is slowing down the test execution\. - --diagnostic-output-directory - Arity: 1 - Hidden: False - Description: Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory\. - --diagnostic-output-fileprefix - Arity: 1 - Hidden: False - Description: Prefix for the log file name that will replace '\[log\]_\.' - --diagnostic-verbosity - Arity: 1 - Hidden: False - Description: Define the level of the verbosity for the --diagnostic\. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical' - --exit-on-process-exit - Arity: 1 - Hidden: False - Description: Exit the test process if dependent process exits\. PID must be provided\. - --help - Arity: 0 - Hidden: False - Description: Show the command line help\. - --ignore-exit-code - Arity: 1 - Hidden: False - Description: Do not report non successful exit value for specific exit codes \(e\.g\. '--ignore-exit-code 8;9' ignore exit code 8 and 9 and will return 0 in these case\) - --info - Arity: 0 - Hidden: False - Description: Display \.NET test application information\. - --internal-testhostcontroller-pid - Arity: 0\.\.1 - Hidden: True - Description: Eventual parent test host controller PID\. - --internal-testingplatform-skipbuildercheck - Arity: 0 - Hidden: True - Description: For testing purposes - --internal-vstest-adapter - Arity: 0 - Hidden: True - Description: Bridge to VSTest APIs - --list-tests - Arity: 0 - Hidden: False - Description: List available tests\. - --minimum-expected-tests - Arity: 0\.\.1 - Hidden: False - Description: Specifies the minimum number of tests that are expected to run\. - --no-banner - Arity: 0\.\.1 - Hidden: True - Description: Do not display the startup banner, the copyright message or the telemetry banner\. - --port - Arity: 1 - Hidden: True - Description: Specify the port of the server\. - --results-directory - Arity: 1 - Hidden: False - Description: The directory where the test results are going to be placed\. If the specified directory doesn't exist, it's created\. The default is TestResults in the directory that contains the test application\. - --server - Arity: 0 - Hidden: True - Description: Enable the server mode\. -Registered command line providers: - TestingFrameworkExtension - Name: Microsoft Testing Framework - Version: .+ - Description: Microsoft Testing Framework\. This framework allows you to test your code anywhere in any mode \(all OSes, all platforms, all configurations\.\.\.\)\. - Options: - --treenode-filter - Arity: 0\.\.1 - Hidden: False - Description: Use a tree filter to filter down the tests to execute -Registered tools: - There are no registered tools\. -"""; - - testHostResult.AssertOutputMatchesRegex(regexMatchPattern); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task Info_WhenMSTestExtensionRegistered_OutputInfoContentOfRegisteredExtension(string tfm) - { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.MSTestTargetAssetPath, TestAssetFixture.MSTestAssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--info"); - - testHostResult.AssertExitCodeIs(ExitCodes.Success); - - const string RegexMatchPattern = $""" -Registered command line providers: - MSTestExtension - Name: MSTest - Version: .+ - Description: MSTest Framework for Microsoft Testing Platform - Options: - --settings - Arity: 0\.\.1 - Hidden: False - Description: The path, relative or absolute, to the \.runsettings file\.For more information and examples on how to configure test run, see https://learn\.microsoft\.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#the-runsettings-file - --filter - Arity: 0\.\.1 - Hidden: False - Description: Filters tests using the given expression\. For more information, see the Filter option details section\. For more information and examples on how to use selective unit test filtering, see https://learn\.microsoft\.com/dotnet/core/testing/selective-unit-tests\. -"""; - - testHostResult.AssertOutputMatchesRegex(RegexMatchPattern); - } - - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) - { - public const string NoExtensionAssetName = "NoExtensionInfoTest"; - public const string MSTestAssetName = "MSTestInfoTest"; - - private const string NoExtensionTestCode = """ -#file NoExtensionInfoTest.csproj - - - $TargetFrameworks$ - enable - enable - Exe - true - preview - - - - - - - - -#file Program.cs -using NoExtensionInfoTest; -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); - -#file UnitTest1.cs -namespace NoExtensionInfoTest; - -[TestGroup] -public class UnitTest1 -{ - public void TestMethod1() - { - Assert.IsTrue(true); - } -} - -#file Usings.cs -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Internal.Framework; -global using Microsoft.Testing.Extensions; -"""; - - private const string MSTestCode = """ -#file MSTestInfoTest.csproj - - - $TargetFrameworks$ - enable - enable - Exe - true - preview - true - - - - - - - - - - -#file Program.cs -using MSTestInfoTest; - -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddMSTest(() => new[] { typeof(Program).Assembly }); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); - -#file UnitTest1.cs -namespace MSTestInfoTest; - -[TestClass] -public class UnitTest1 -{ - [TestMethod] - public void TestMethod1() - { - } -} - -#file Usings.cs -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.VisualStudio.TestTools.UnitTesting; -"""; - - public string NoExtensionTargetAssetPath => GetAssetPath(NoExtensionAssetName); - - public string MSTestTargetAssetPath => GetAssetPath(MSTestAssetName); - - public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() - { - yield return (NoExtensionAssetName, NoExtensionAssetName, - NoExtensionTestCode - .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion)); - yield return (MSTestAssetName, MSTestAssetName, - MSTestCode - .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); - } - } -} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuild.KnownExtensionRegistration.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuild.KnownExtensionRegistration.cs new file mode 100644 index 0000000000..b210d44b2e --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuild.KnownExtensionRegistration.cs @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; + +using SL = Microsoft.Build.Logging.StructuredLogger; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public class MSBuildTests_KnownExtensionRegistration : AcceptanceTestBase +{ + private readonly AcceptanceFixture _acceptanceFixture; + private const string AssetName = "MSBuildTests"; + + public MSBuildTests_KnownExtensionRegistration(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) + : base(testExecutionContext) + { + _acceptanceFixture = acceptanceFixture; + } + + [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + public async Task Microsoft_Testing_Platform_Extensions_ShouldBe_Correctly_Registered(string tfm, BuildConfiguration compilationMode, Verb verb) + => await RetryHelper.RetryAsync( + async () => + { + TestAsset testAsset = await TestAsset.GenerateAssetAsync( + nameof(Microsoft_Testing_Platform_Extensions_ShouldBe_Correctly_Registered), + SourceCode + .PatchCodeWithReplace("$TargetFrameworks$", tfm) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -r {RID} {testAsset.TargetAssetPath}{Path.DirectorySeparatorChar}MSBuildTests.csproj", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false -bl:{binlogFile} {testAsset.TargetAssetPath} -v:n", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + + var testHost = TestInfrastructure.TestHost.LocateFrom(testAsset.TargetAssetPath, AssetName, tfm, rid: RID, verb: verb, buildConfiguration: compilationMode); + TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); + testHostResult.AssertOutputContains("--crashdump"); + testHostResult.AssertOutputContains("--report-trx"); + testHostResult.AssertOutputContains("--retry-failed-tests"); + testHostResult.AssertOutputContains("--hangdump"); + + SL.Build binLog = SL.Serialization.Read(binlogFile); + SL.Target generateTestingPlatformEntryPoint = binLog.FindChildrenRecursive().Single(t => t.Name == "_GenerateTestingPlatformEntryPoint"); + SL.Task testingPlatformEntryPoint = generateTestingPlatformEntryPoint.FindChildrenRecursive().Single(t => t.Name == "TestingPlatformEntryPointTask"); + SL.Message generatedSource = testingPlatformEntryPoint.FindChildrenRecursive().Single(m => m.Text.Contains("Entrypoint source:")); + + Assert.IsTrue(generatedSource.Text.Contains("Microsoft.Testing.Extensions.CrashDump.TestingPlatformBuilderHook.AddExtensions"), generatedSource.Text); + Assert.IsTrue(generatedSource.Text.Contains("Microsoft.Testing.Extensions.HangDump.TestingPlatformBuilderHook.AddExtensions"), generatedSource.Text); + Assert.IsTrue(generatedSource.Text.Contains("Microsoft.Testing.Extensions.HotReload.TestingPlatformBuilderHook.AddExtensions"), generatedSource.Text); + Assert.IsTrue(generatedSource.Text.Contains("Microsoft.Testing.Extensions.Retry.TestingPlatformBuilderHook.AddExtensions"), generatedSource.Text); + Assert.IsTrue(generatedSource.Text.Contains("Microsoft.Testing.Extensions.Telemetry.TestingPlatformBuilderHook.AddExtensions"), generatedSource.Text); + Assert.IsTrue(generatedSource.Text.Contains("Microsoft.Testing.Extensions.TrxReport.TestingPlatformBuilderHook.AddExtensions"), generatedSource.Text); + }, 3, TimeSpan.FromSeconds(5)); + + private const string SourceCode = """ +#file MSBuildTests.csproj + + + + + DummyAdapter + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration + + + + + $TargetFrameworks$ + enable + enable + preview + Exe + + + + + + + + + + + + + + + +#file Program.cs +using System.Collections.Generic; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +namespace MyNamespaceRoot.Level1.Level2; + +public static class DummyAdapterRegistration +{ + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] args) + { + testApplicationBuilder.RegisterTestFramework(_ => new Capabilities(), (_, __) => new DummyAdapter()); + } +} + +internal sealed class DummyAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyAdapter); + + public string Version => string.Empty; + + public string DisplayName => string.Empty; + + public string Description => string.Empty; + + public Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage) }; + + public Task CloseTestSessionAsync(CloseTestSessionContext context) => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "1", DisplayName = "DummyTest", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); + context.Complete(); + } + + public Task IsEnabledAsync() => Task.FromResult(true); +} + +internal sealed class Capabilities : ITestFrameworkCapabilities +{ + IReadOnlyCollection ICapabilities.Capabilities => Array.Empty(); +} +"""; +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs new file mode 100644 index 0000000000..7d1891c5c7 --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Text.RegularExpressions; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public class MSBuildTests : AcceptanceTestBase +{ + public MSBuildTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) + : base(testExecutionContext) + { + _acceptanceFixture = acceptanceFixture; + } + + [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + public async Task ConfigFileGeneration_CorrectlyCreateAndCacheAndCleaned(string tfm, BuildConfiguration compilationMode, Verb verb) + { + using TestAsset testAsset = await TestAsset.GenerateAssetAsync( + nameof(ConfigFileGeneration_CorrectlyCreateAndCacheAndCleaned), + SourceCode + .PatchCodeWithReplace("$TargetFrameworks$", tfm) + .PatchCodeWithReplace("$JsonContent$", ConfigurationContent) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:normal -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + + var testHost = TestInfrastructure.TestHost.LocateFrom(testAsset.TargetAssetPath, "MSBuildTests", tfm, verb: verb, buildConfiguration: compilationMode); + string generatedConfigurationFile = Path.Combine(testHost.DirectoryName, "MSBuildTests.testingplatformconfig.json"); + Assert.IsTrue(File.Exists(generatedConfigurationFile)); + Assert.AreEqual(ConfigurationContent.Trim(), File.ReadAllText(generatedConfigurationFile).Trim()); + Assert.IsTrue(compilationResult.StandardOutput.Contains("Microsoft Testing Platform configuration file written")); + + compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:normal -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + Assert.IsTrue(File.Exists(generatedConfigurationFile)); + Assert.AreEqual(ConfigurationContent.Trim(), File.ReadAllText(generatedConfigurationFile).Trim()); + compilationResult.StandardOutput.Contains("Microsoft Testing Platform configuration file written"); + Assert.IsTrue(Regex.IsMatch( + compilationResult.StandardOutput, + """ +\s*GenerateTestingPlatformConfigurationFile: +\s*Skipping target "GenerateTestingPlatformConfigurationFile" because all output files are up\-to\-date with respect to the input files\. +""")); + compilationResult = await DotnetCli.RunAsync($"clean -c {compilationMode} -v:normal {testAsset.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + + // dotnet clean doesn't clean the publish output folder + if (verb == Verb.build) + { + Assert.IsFalse(File.Exists(generatedConfigurationFile)); + } + } + + [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + public async Task ConfigFileGeneration_NoConfigurationFile_TaskWontRun(string tfm, BuildConfiguration compilationMode, Verb verb) + { + using TestAsset testAsset = await TestAsset.GenerateAssetAsync( + nameof(ConfigFileGeneration_NoConfigurationFile_TaskWontRun), + SourceCode + .PatchCodeWithReplace("$TargetFrameworks$", tfm) + .PatchCodeWithReplace("$JsonContent$", ConfigurationContent) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + + File.Delete(Path.Combine(testAsset.TargetAssetPath, "testingplatformconfig.json")); + + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:diagnostic -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + + var testHost = TestInfrastructure.TestHost.LocateFrom(testAsset.TargetAssetPath, "MSBuildTests", tfm, verb: verb, buildConfiguration: compilationMode); + Assert.IsTrue(compilationResult.StandardOutput.Contains("Target \"GenerateTestingPlatformConfigurationFile\" skipped, due to false condition;")); + string generatedConfigurationFile = Path.Combine(testHost.DirectoryName, "MSBuildTests.testingplatformconfig.json"); + Assert.IsFalse(File.Exists(generatedConfigurationFile)); + } + + private const string ConfigurationContent = """ +{ + "testingplatform": { + "exitProcessOnUnhandledException": true + } +} +"""; + + private const string SourceCode = """ +#file MSBuildTests.csproj + + + $TargetFrameworks$ + enable + enable + Exe + true + preview + false + + + + + + + + + + + +#file testingplatformconfig.json +$JsonContent$ + +#file Program.cs +using MSBuildTests; +ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); +builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); +using ITestApplication app = await builder.BuildAsync(); +return await app.RunAsync(); + +#file UnitTest1.cs +namespace MSBuildTests; + +[TestGroup] +public class UnitTest1 +{ + public void TestMethod1() + { + Assert.IsTrue(true); + } +} + +#file Usings.cs +global using Microsoft.Testing.Platform.Builder; +global using Microsoft.Testing.Internal.Framework; +"""; + + private readonly AcceptanceFixture _acceptanceFixture; +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.GenerateEntryPoint.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.GenerateEntryPoint.cs new file mode 100644 index 0000000000..e4718177b4 --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.GenerateEntryPoint.cs @@ -0,0 +1,462 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +using SL = Microsoft.Build.Logging.StructuredLogger; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public class MSBuildTests_EntryPoint : AcceptanceTestBase +{ + private const string AssetName = "MSBuildTests"; + private readonly AcceptanceFixture _acceptanceFixture; + + public MSBuildTests_EntryPoint(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) + : base(testExecutionContext) + { + _acceptanceFixture = acceptanceFixture; + } + + [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + public async Task When_GenerateTestingPlatformEntryPoint_IsFalse_NoEntryPointInjected(string tfm, BuildConfiguration compilationMode, Verb verb) + => await RetryHelper.RetryAsync( + async () => + { + using TestAsset testAsset = await TestAsset.GenerateAssetAsync( + nameof(GenerateCSharpEntryPointAndVerifyTheCacheUsage), + CSharpSourceCode + .PatchCodeWithReplace("$TargetFrameworks$", tfm) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -r {RID} {testAsset.TargetAssetPath}{Path.DirectorySeparatorChar}MSBuildTests.csproj ", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); + compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false -p:GenerateTestingPlatformEntryPoint=False -bl:{binlogFile} {testAsset.TargetAssetPath} -v:n", _acceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); + SL.Build binLog = SL.Serialization.Read(binlogFile); + SL.Target generateTestingPlatformEntryPoint = binLog.FindChildrenRecursive().Single(t => t.Name == "_GenerateTestingPlatformEntryPoint"); + Assert.AreEqual("Target \"_GenerateTestingPlatformEntryPoint\" skipped, due to false condition; ( '$(GenerateTestingPlatformEntryPoint)' == 'True' ) was evaluated as ( 'False' == 'True' ).", ((SL.Message)generateTestingPlatformEntryPoint.Children[0]).Text); + SL.Target generateTestingPlatformEntryPointFileInputCache = binLog.FindChildrenRecursive().Single(t => t.Name == "_GenerateTestingPlatformEntryPointFileInputCache"); + Assert.AreEqual("Target \"_GenerateTestingPlatformEntryPointFileInputCache\" skipped, due to false condition; ( '$(GenerateTestingPlatformEntryPoint)' == 'True' ) was evaluated as ( 'False' == 'True' ).", ((SL.Message)generateTestingPlatformEntryPointFileInputCache.Children[0]).Text); + SL.Target includeGenerateTestingPlatformEntryPointIntoCompilation = binLog.FindChildrenRecursive().Single(t => t.Name == "IncludeGenerateTestingPlatformEntryPointIntoCompilation"); + Assert.AreEqual("Target \"IncludeGenerateTestingPlatformEntryPointIntoCompilation\" skipped, due to false condition; ( '$(GenerateTestingPlatformEntryPoint)' == 'True' ) was evaluated as ( 'False' == 'True' ).", ((SL.Message)includeGenerateTestingPlatformEntryPointIntoCompilation.Children[0]).Text); + Assert.AreNotEqual(0, compilationResult.ExitCode); + }, 3, TimeSpan.FromSeconds(10)); + + [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + public async Task GenerateCSharpEntryPointAndVerifyTheCacheUsage(string tfm, BuildConfiguration compilationMode, Verb verb) + => await GenerateAndVerifyLanguageSpecificEntryPoint(nameof(GenerateCSharpEntryPointAndVerifyTheCacheUsage), CSharpSourceCode, "cs", tfm, compilationMode, verb, + @"Entrypoint source: +'//------------------------------------------------------------------------------ +// +// This code was generated by Microsoft.Testing.Platform.MSBuild +// +//------------------------------------------------------------------------------ + +[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] +internal sealed class TestingPlatformEntryPoint +{ + public static async global::System.Threading.Tasks.Task Main(string[] args) + { + global::Microsoft.Testing.Platform.Builder.ITestApplicationBuilder builder = await global::Microsoft.Testing.Platform.Builder.TestApplication.CreateBuilderAsync(args); + Microsoft.Testing.Platform.MSBuild.TestingPlatformBuilderHook.AddExtensions(builder, args); + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration.AddExtensions(builder, args); + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration2.AddExtensions(builder, args); + using (global::Microsoft.Testing.Platform.Builder.ITestApplication app = await builder.BuildAsync()) + { + return await app.RunAsync(); + } + } +}'", "Csc"); + + [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + public async Task GenerateVBEntryPointAndVerifyTheCacheUsage(string tfm, BuildConfiguration compilationMode, Verb verb) + => await GenerateAndVerifyLanguageSpecificEntryPoint(nameof(GenerateVBEntryPointAndVerifyTheCacheUsage), VBSourceCode, "vb", tfm, compilationMode, verb, + @"Entrypoint source: +''------------------------------------------------------------------------------ +' +' This code was generated by Microsoft.Testing.Platform.MSBuild +' +'------------------------------------------------------------------------------ + + +Module TestingPlatformEntryPoint + + Function Main(args As Global.System.String()) As Global.System.Int32 + Return MainAsync(args).Result + End Function + + Public Async Function MainAsync(ByVal args() As Global.System.String) As Global.System.Threading.Tasks.Task(Of Integer) + Dim builder = Await Global.Microsoft.Testing.Platform.Builder.TestApplication.CreateBuilderAsync(args) + Microsoft.Testing.Platform.MSBuild.TestingPlatformBuilderHook.AddExtensions(builder, args) + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration.AddExtensions(builder, args) + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration2.AddExtensions(builder, args) + Using testApplication = Await builder.BuildAsync() + Return Await testApplication.RunAsync() + End Using + End Function + +End Module'", "Vbc"); + + [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + public async Task GenerateFSharpEntryPointAndVerifyTheCacheUsage(string tfm, BuildConfiguration compilationMode, Verb verb) + => await GenerateAndVerifyLanguageSpecificEntryPoint(nameof(GenerateFSharpEntryPointAndVerifyTheCacheUsage), FSharpSourceCode, "fs", tfm, compilationMode, verb, + @"Entrypoint source: +'//------------------------------------------------------------------------------ +// +// This code was generated by Microsoft.Testing.Platform.MSBuild +// +//------------------------------------------------------------------------------ + +[] +[] +let main args = + task { + let! builder = Microsoft.Testing.Platform.Builder.TestApplication.CreateBuilderAsync args + Microsoft.Testing.Platform.MSBuild.TestingPlatformBuilderHook.AddExtensions(builder, args) + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration.AddExtensions(builder, args) + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration2.AddExtensions(builder, args) + use! app = builder.BuildAsync() + return! app.RunAsync() + } + |> Async.AwaitTask + |> Async.RunSynchronously'", "Fsc"); + + private async Task GenerateAndVerifyLanguageSpecificEntryPoint(string assetName, string sourceCode, string languageFileExtension, string tfm, + BuildConfiguration compilationMode, Verb verb, string expectedEntryPoint, string cscProcessName) + => await RetryHelper.RetryAsync( + async () => + { + using TestAsset testAsset = await TestAsset.GenerateAssetAsync( + assetName, + sourceCode + .PatchCodeWithReplace("$TargetFrameworks$", tfm) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -r {RID} {testAsset.TargetAssetPath}{Path.DirectorySeparatorChar}MSBuildTests.{languageFileExtension}proj", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); + compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false -bl:{binlogFile} {testAsset.TargetAssetPath} -v:n", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + SL.Build binLog = SL.Serialization.Read(binlogFile); + SL.Target generateTestingPlatformEntryPoint = binLog.FindChildrenRecursive().Single(t => t.Name == "_GenerateTestingPlatformEntryPoint"); + SL.Task testingPlatformEntryPoint = generateTestingPlatformEntryPoint.FindChildrenRecursive().Single(t => t.Name == "TestingPlatformEntryPointTask"); + SL.Message generatedSource = testingPlatformEntryPoint.FindChildrenRecursive().Single(m => m.Text.Contains("Entrypoint source:")); + Assert.AreEqual(expectedEntryPoint.ReplaceLineEndings(), generatedSource.Text.ReplaceLineEndings()); + + var testHost = TestInfrastructure.TestHost.LocateFrom(testAsset.TargetAssetPath, AssetName, tfm, rid: RID, verb: verb, buildConfiguration: compilationMode); + TestHostResult testHostResult = await testHost.ExecuteAsync(); + testHostResult.AssertExitCodeIs(ExitCodes.Success); + Assert.IsTrue(testHostResult.StandardOutput.Contains("Passed!")); + + SL.Target coreCompile = binLog.FindChildrenRecursive().Single(t => t.Name == "CoreCompile" && t.Children.Count > 0); + SL.Task csc = coreCompile.FindChildrenRecursive(t => t.Name == cscProcessName).Single(); + SL.Parameter sources = csc.FindChildrenRecursive(t => t.Name == "Sources").Single(); + string? sourceFilePathInObj = sources.FindChildrenRecursive(i => i.Text.EndsWith($"TestPlatformEntryPoint.{languageFileExtension}", StringComparison.OrdinalIgnoreCase)).SingleOrDefault()?.Text; + Assert.IsNotNull(sourceFilePathInObj); + + File.Delete(binlogFile); + compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false -bl:{binlogFile} {testAsset.TargetAssetPath} -v:n", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + binLog = SL.Serialization.Read(binlogFile); + generateTestingPlatformEntryPoint = binLog.FindChildrenRecursive(t => t.Name == "_GenerateTestingPlatformEntryPoint" && t.Children.Count > 0).Single(); + Assert.IsNotNull(generateTestingPlatformEntryPoint.FindChildrenRecursive(m => m.Text.Contains("Skipping target \"_GenerateTestingPlatformEntryPoint\" because all output files are up-to-date with respect to the input files.", StringComparison.OrdinalIgnoreCase)).Single()); + + testHost = TestInfrastructure.TestHost.LocateFrom(testAsset.TargetAssetPath, AssetName, tfm, rid: RID, verb: verb, buildConfiguration: compilationMode); + testHostResult = await testHost.ExecuteAsync(); + Assert.AreEqual(ExitCodes.Success, testHostResult.ExitCode); + Assert.IsTrue(testHostResult.StandardOutput.Contains("Passed!")); + }, 3, TimeSpan.FromSeconds(10)); + + private const string CSharpSourceCode = """ +#file MSBuildTests.csproj + + + + + DummyAdapter + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration + + + + + + DummyAdapter2 + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration2 + + + + + $TargetFrameworks$ + enable + enable + preview + Exe + + + + + + + + +#file Program.cs +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Requests; + +namespace MyNamespaceRoot.Level1.Level2; + +public static class DummyAdapterRegistration +{ + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] args) + { + testApplicationBuilder.RegisterTestFramework(_ => new Capabilities(), (_, __) => new DummyAdapter()); + } +} + +public static class DummyAdapterRegistration2 +{ + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] args) + { + + } +} + +internal sealed class DummyAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyAdapter); + + public string Version => string.Empty; + + public string DisplayName => string.Empty; + + public string Description => string.Empty; + + public Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage) }; + + public Task CloseTestSessionAsync(CloseTestSessionContext context) => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "1", DisplayName = "DummyTest", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); + context.Complete(); + } + + public Task IsEnabledAsync() => Task.FromResult(true); +} + +internal sealed class Capabilities : ITestFrameworkCapabilities +{ + IReadOnlyCollection ICapabilities.Capabilities => Array.Empty(); +} +"""; + + private const string VBSourceCode = """ +#file MSBuildTests.vbproj + + + + + DummyAdapter + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration + + + + + + DummyAdapter2 + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration2 + + + + + $TargetFrameworks$ + Exe + + + + + + + + +#file Program.vb +Imports Microsoft.Testing.Platform.Capabilities.TestFramework +Imports Microsoft.Testing.Platform.Extensions + +Namespace MyNamespaceRoot.Level1.Level2 + Public Module DummyAdapterRegistration + Public Sub AddExtensions(builder As Microsoft.Testing.Platform.Builder.ITestApplicationBuilder, args As String()) + builder.RegisterTestFramework(Function() New Capabilities(), Function(cap, services) New DummyAdapter()) + End Sub + End Module + + Public Module DummyAdapterRegistration2 + Public Sub AddExtensions(builder As Microsoft.Testing.Platform.Builder.ITestApplicationBuilder, args As String()) + End Sub + End Module + + Class DummyAdapter + Implements TestFramework.ITestFramework + Implements Messages.IDataProducer + + Public ReadOnly Property Uid As String Implements IExtension.Uid + Get + Return String.Empty + End Get + End Property + + Public ReadOnly Property Version As String Implements IExtension.Version + Get + Return String.Empty + End Get + End Property + + Public ReadOnly Property DisplayName As String Implements IExtension.DisplayName + Get + Return String.Empty + End Get + End Property + + Public ReadOnly Property Description As String Implements IExtension.Description + Get + Return String.Empty + End Get + End Property + + Public ReadOnly Property DataTypesProduced As Type() Implements Messages.IDataProducer.DataTypesProduced + Get + Dim types(1) As Type + types(0) = GetType(Messages.TestNodeUpdateMessage) + Return types + End Get + End Property + + Public Function CreateTestSessionAsync(context As TestFramework.CreateTestSessionContext) As Task(Of TestFramework.CreateTestSessionResult) Implements TestFramework.ITestFramework.CreateTestSessionAsync + Dim ctx As New TestFramework.CreateTestSessionResult() + ctx.IsSuccess = True + Return Task.FromResult(ctx) + End Function + + Public Async Function ExecuteRequestAsync(context As TestFramework.ExecuteRequestContext) As Task Implements TestFramework.ITestFramework.ExecuteRequestAsync + Await context.MessageBus.PublishAsync(Me, New Messages.TestNodeUpdateMessage(context.Request.Session.SessionUid, + New Messages.TestNode With { + .Uid = "1", + .DisplayName = "DummyTest", + .Properties = New Messages.PropertyBag(Messages.PassedTestNodeStateProperty.CachedInstance)})) + context.Complete() + End Function + + Public Function CloseTestSessionAsync(context As TestFramework.CloseTestSessionContext) As Task(Of TestFramework.CloseTestSessionResult) Implements TestFramework.ITestFramework.CloseTestSessionAsync + Dim ctx As New TestFramework.CloseTestSessionResult() + ctx.IsSuccess = True + Return Task.FromResult(ctx) + End Function + + Public Function IsEnabledAsync() As Task(Of Boolean) Implements IExtension.IsEnabledAsync + Return Task(Of Boolean).FromResult(True) + End Function + End Class + + Class Capabilities + Implements Microsoft.Testing.Platform.Capabilities.TestFramework.ITestFrameworkCapabilities + + Private ReadOnly Property ICapabilities_Capabilities As IReadOnlyCollection(Of ITestFrameworkCapability) Implements Microsoft.Testing.Platform.Capabilities.ICapabilities(Of ITestFrameworkCapability).Capabilities + Get + Return Array.Empty(Of ITestFrameworkCapability)() + End Get + End Property + End Class +End Namespace +"""; + + private const string FSharpSourceCode = """ +#file MSBuildTests.fsproj + + + + + DummyAdapter + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration + + + + + + DummyAdapter2 + MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration2 + + + + + $TargetFrameworks$ + preview + Exe + + + + + + + + + + + + +#file Program.fs +namespace MyNamespaceRoot.Level1.Level2 + +open Microsoft.Testing.Platform.Builder +open Microsoft.Testing.Platform.Capabilities.TestFramework +open Microsoft.Testing.Platform.Extensions.Messages +open Microsoft.Testing.Platform.Extensions.TestFramework +open System.Threading.Tasks + +type Capabilities () = + interface ITestFrameworkCapabilities with + member _.Capabilities = [||] + +type DummyAdapter() = + let dataProducer = { + new IDataProducer with + member _.DataTypesProduced = [| typedefof |] + member _.Uid = nameof(DummyAdapter) + member _.Version = "" + member _.DisplayName = "" + member _.Description = "" + + member _.IsEnabledAsync() = Task.FromResult true + } + + interface ITestFramework with + member _.Uid = nameof(DummyAdapter) + member _.Version = "" + member _.DisplayName = "" + member _.Description = "" + + member _.IsEnabledAsync() = Task.FromResult true + member _.CreateTestSessionAsync _ = CreateTestSessionResult(IsSuccess = true) |> Task.FromResult + member _.CloseTestSessionAsync _ = CloseTestSessionResult(IsSuccess = true) |> Task.FromResult + member _.ExecuteRequestAsync context = task { + do! context.MessageBus.PublishAsync( + dataProducer, + TestNodeUpdateMessage( + context.Request.Session.SessionUid, + TestNode(Uid = "1", DisplayName = "DummyTest", Properties = PropertyBag(PassedTestNodeStateProperty.CachedInstance)))) + context.Complete() + } + +module DummyAdapterRegistration = + let AddExtensions (testApplicationBuilder : ITestApplicationBuilder, args: string[]) = + testApplicationBuilder.RegisterTestFramework((fun _ -> Capabilities()), (fun _ _ -> DummyAdapter())) + +module DummyAdapterRegistration2 = + let AddExtensions (_, _) = () + +"""; +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs new file mode 100644 index 0000000000..13c3ee5d6e --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs @@ -0,0 +1,373 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public class MSBuildTests_Solution : AcceptanceTestBase +{ + private readonly AcceptanceFixture _acceptanceFixture; + private const string AssetName = "MSTestProject"; + + static MSBuildTests_Solution() + { + string dotnetMuxerSDK = Directory.GetDirectories(Path.Combine(RootFinder.Find(), ".dotnet", "sdk")).OrderByDescending(x => x).First(); + File.WriteAllText(Path.Combine(dotnetMuxerSDK, "Microsoft.Common.CrossTargeting.targets"), MicrosoftCommonCrossTargeting); + if (!File.Exists(Path.Combine(dotnetMuxerSDK, "Microsoft.Common.Test.targets"))) + { + File.WriteAllText(Path.Combine(dotnetMuxerSDK, "Microsoft.Common.Test.targets"), MicrosoftCommonTesttargets); + } + } + + public MSBuildTests_Solution(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) + : base(testExecutionContext) + { + _acceptanceFixture = acceptanceFixture; + } + + private void CheckPatch() + { + // https://github.com/dotnet/sdk/issues/37712 + if (DateTime.UtcNow.Date > new DateTime(2024, 9, 1)) + { + throw new InvalidOperationException("Check if we can remove the patch!"); + } + } + + internal static IEnumerable> GetBuildMatrix() + { + foreach (TestArgumentsEntry<(string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm)> entry in GetBuildMatrixSingleAndMultiTfmBuildConfiguration()) + { + foreach (string command in new string[] + { + "build --no-restore -t:Test -p:UseMSBuildTestInfrastructure=true", + "test --no-restore", + }) + { + yield return new TestArgumentsEntry<(string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm, string Command)>( + (entry.Arguments.SingleTfmOrMultiTfm, entry.Arguments.BuildConfiguration, entry.Arguments.IsMultiTfm, command), $"{(entry.Arguments.IsMultiTfm ? "multitfm" : entry.Arguments.SingleTfmOrMultiTfm)},{entry.Arguments.BuildConfiguration},{command}"); + } + } + } + + [ArgumentsProvider(nameof(GetBuildMatrix))] + [NonTest] // TODO: AMAURY/MARCO - Investigate test + public async Task MSBuildTests_UseMSBuildTestInfrastructure_Should_Run_Solution_Tests(string singleTfmOrMultiTfm, BuildConfiguration _, bool isMultiTfm, string command) + { + CheckPatch(); + + using TestAsset generator = await TestAsset.GenerateAssetAsync( + AssetName, + SourceCode + .PatchCodeWithReplace("$TargetFrameworks$", isMultiTfm ? $"{singleTfmOrMultiTfm}" : $"{singleTfmOrMultiTfm}") + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + + string projectContent = File.ReadAllText(Directory.GetFiles(generator.TargetAssetPath, "MSBuildTests.csproj", SearchOption.AllDirectories).Single()); + string programSourceContent = File.ReadAllText(Directory.GetFiles(generator.TargetAssetPath, "Program.cs", SearchOption.AllDirectories).Single()); + string unitTestSourceContent = File.ReadAllText(Directory.GetFiles(generator.TargetAssetPath, "UnitTest1.cs", SearchOption.AllDirectories).Single()); + string usingsSourceContent = File.ReadAllText(Directory.GetFiles(generator.TargetAssetPath, "Usings.cs", SearchOption.AllDirectories).Single()); + string nugetConfigContent = File.ReadAllText(Directory.GetFiles(generator.TargetAssetPath, "NuGet.config", SearchOption.AllDirectories).Single()); + + // Create a solution with 3 projects + using TempDirectory tempDirectory = new(); + string solutionFolder = Path.Combine(tempDirectory.Path, "Solution"); + VSSolution solution = new(solutionFolder, "MSTestSolution"); + string nugetFile = solution.AddOrUpdateFileContent("Nuget.config", nugetConfigContent); + for (int i = 0; i < 3; i++) + { + CSharpProject project = solution.CreateCSharpProject($"TestProject{i}", isMultiTfm ? singleTfmOrMultiTfm.Split(';') : [singleTfmOrMultiTfm]); + File.WriteAllText(project.ProjectFile, projectContent); + project.AddOrUpdateFileContent("Program.cs", programSourceContent.PatchCodeWithReplace("$ProjectName$", $"TestProject{i}")); + project.AddOrUpdateFileContent("UnitTest1.cs", unitTestSourceContent); + project.AddOrUpdateFileContent("Usings.cs", usingsSourceContent); + + CSharpProject project2 = solution.CreateCSharpProject($"Project{i}", isMultiTfm ? singleTfmOrMultiTfm.Split(';') : [singleTfmOrMultiTfm]); + project.AddProjectReference(project2.ProjectFile); + } + + // Build the solution + DotnetMuxerResult restoreResult = await DotnetCli.RunAsync($"restore -nodeReuse:false {solution.SolutionFile} --configfile {nugetFile}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + restoreResult.AssertOutputNotContains("An approximate best match of"); + DotnetMuxerResult testResult = await DotnetCli.RunAsync($"{command} -nodeReuse:false {solution.SolutionFile}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + + if (isMultiTfm) + { + foreach (string tfm in singleTfmOrMultiTfm.Split(';')) + { + testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject0\..*' \[{tfm}\|x64\]"); + testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject1\..*' \[{tfm}\|x64\]"); + testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject2\..*' \[{tfm}\|x64\]"); + } + } + else + { + testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject0\..*' \[{singleTfmOrMultiTfm}\|x64\]"); + testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject1\..*' \[{singleTfmOrMultiTfm}\|x64\]"); + testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject2\..*' \[{singleTfmOrMultiTfm}\|x64\]"); + } + } + + private const string SourceCode = """ +#file MSBuildTests.csproj + + + $TargetFrameworks$ + enable + enable + Exe + true + preview + x64 + false + true + + + + + + + + + + + +#file Program.cs +using MSBuildTests; +using $ProjectName$; +ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); +builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); +builder.AddMSBuild(); +using ITestApplication app = await builder.BuildAsync(); +return await app.RunAsync(); + +#file UnitTest1.cs +namespace MSBuildTests; + +[TestGroup] +public class UnitTest1 +{ + public void TestMethod1() + { + Assert.IsTrue(true); + } +} + +#file Usings.cs +global using Microsoft.Testing.Platform.Builder; +global using Microsoft.Testing.Internal.Framework; +global using Microsoft.Testing.Platform.MSBuild; +"""; + + private const string MicrosoftCommonCrossTargeting = """ + + + + true + true + + + + + + + + + + <_ThisProjectBuildMetadata Include="$(MSBuildProjectFullPath)"> + @(_TargetFrameworkInfo) + @(_TargetFrameworkInfo->'%(TargetFrameworkMonikers)') + @(_TargetFrameworkInfo->'%(TargetPlatformMonikers)') + $(_AdditionalPropertiesFromProject) + false + @(_TargetFrameworkInfo->'%(IsRidAgnostic)') + + + false + $(Platform) + $(Platforms) + + + + + + <_TargetFramework Include="$(TargetFrameworks)" /> + + <_TargetFrameworkNormalized Include="@(_TargetFramework->Trim()->Distinct())" /> + <_InnerBuildProjects Include="$(MSBuildProjectFile)"> + TargetFramework=%(_TargetFrameworkNormalized.Identity) + + + + + + + + + + + + true + + + + + + + + + + + + + Build + + + + + + + + + + + + $([MSBuild]::IsRunningFromVisualStudio()) + $([MSBuild]::GetToolsDirectory32())\..\..\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets + $(MSBuildToolsPath)\NuGet.targets + + + + + + true + + + + + true + + + + true + + + + <_DirectoryBuildTargetsFile Condition="'$(_DirectoryBuildTargetsFile)' == ''">Directory.Build.targets + <_DirectoryBuildTargetsBasePath Condition="'$(_DirectoryBuildTargetsBasePath)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), '$(_DirectoryBuildTargetsFile)')) + $([System.IO.Path]::Combine('$(_DirectoryBuildTargetsBasePath)', '$(_DirectoryBuildTargetsFile)')) + + + + false + + + +"""; + + private const string MicrosoftCommonTesttargets = """ + + + +"""; +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Test.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Test.cs new file mode 100644 index 0000000000..e624cbd9a9 --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Test.cs @@ -0,0 +1,273 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public class MSBuildTests_Test : AcceptanceTestBase +{ + private const string AssetName = "MSBuildTests"; + private readonly AcceptanceFixture _acceptanceFixture; + + public MSBuildTests_Test(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) + : base(testExecutionContext) + { + _acceptanceFixture = acceptanceFixture; + } + + internal static TestArgumentsEntry<(string BuildCommand, string TargetFramework, BuildConfiguration BuildConfiguration, bool TestSucceeded)> FormatBuildMatrixEntry(TestArgumentsContext ctx) + { + var entry = ((string, string, BuildConfiguration, bool))ctx.Arguments; + return new TestArgumentsEntry<(string, string, BuildConfiguration, bool)>(entry, $"{entry.Item1},{(TargetFrameworks.All.ToMSBuildTargetFrameworks() == entry.Item2 ? "multitfm" : entry.Item2)},{entry.Item3},{(entry.Item4 ? "Succeeded" : "Failed")}"); + } + + internal static IEnumerable GetBuildConfiguration() + { + string[] compilationModes = ["Debug", "Release"]; + foreach (string compilationMode in compilationModes) + { + yield return compilationMode == "Debug" ? BuildConfiguration.Debug : BuildConfiguration.Release; + } + } + + internal static IEnumerable<(string BuildCommand, string TargetFramework, BuildConfiguration BuildConfiguration, bool TestSucceeded)> GetBuildMatrix() + { + foreach (TestArgumentsEntry tfm in TargetFrameworks.All) + { + foreach (BuildConfiguration compilationMode in GetBuildConfiguration()) + { + foreach (bool testSucceeded in new bool[] { true, false }) + { + foreach (string buildCommand in new string[] + { + "build -t:Test", + "test -p:TestingPlatformDotnetTestSupport=True", + }) + { + yield return (buildCommand, tfm.Arguments, compilationMode, testSucceeded); + } + } + } + } + } + + internal static IEnumerable<(string BuildCommand, string TargetFramework, BuildConfiguration BuildConfiguration, bool TestSucceeded)> GetBuildMatrixMultiTfm() + { + foreach (BuildConfiguration compilationMode in GetBuildConfiguration()) + { + foreach (bool testSucceeded in new bool[] { true, false }) + { + foreach (string buildCommand in new string[] + { + "build -t:Test", + "test -p:TestingPlatformDotnetTestSupport=True", + }) + { + yield return (buildCommand, TargetFrameworks.All.ToMSBuildTargetFrameworks(), compilationMode, testSucceeded); + } + } + } + } + + [ArgumentsProvider(nameof(GetBuildMatrix), TestArgumentsEntryProviderMethodName = nameof(FormatBuildMatrixEntry))] + public async Task InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Showing_Error_Detail_SingleTfm(string testCommand, string tfm, BuildConfiguration compilationMode, bool testSucceeded) + => await InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Showing_Error_Detail(testCommand, tfm, false, [tfm], compilationMode, testSucceeded); + + [ArgumentsProvider(nameof(GetBuildMatrixMultiTfm), TestArgumentsEntryProviderMethodName = nameof(FormatBuildMatrixEntry))] + public async Task InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Showing_Error_Detail_MultiTfm(string testCommand, string multiTfm, BuildConfiguration compilationMode, bool testSucceeded) + => await InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Showing_Error_Detail(testCommand, multiTfm, true, TargetFrameworks.All.Select(x => x.Arguments).ToArray(), compilationMode, testSucceeded); + + private async Task InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Showing_Error_Detail(string testCommand, string tfm, bool isMultiTfm, string[] tfmsToAssert, BuildConfiguration compilationMode, bool testSucceeded) + => await RetryHelper.RetryAsync( + async () => + { + using TestAsset testAsset = await TestAsset.GenerateAssetAsync( + AssetName, + SourceCode + .PatchCodeWithReplace("$PlatformTarget$", "x64") + .PatchCodeWithReplace("$TargetFrameworks$", isMultiTfm ? $"{tfm}" : $"{tfm}") + .PatchCodeWithReplace("$AssertValue$", testSucceeded.ToString().ToLowerInvariant()) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); + string testResultFolder = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N")); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{testCommand} -p:TestingPlatformCommandLineArguments=\"--results-directory %22{testResultFolder}%22\" -p:Configuration={compilationMode} -p:nodeReuse=false -bl:{binlogFile} \"{testAsset.TargetAssetPath}\"", _acceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); + + foreach (string tfmToAssert in tfmsToAssert) + { + CommonAssert(compilationResult, tfmToAssert, testSucceeded, testResultFolder); + } + }, 3, TimeSpan.FromSeconds(10)); + + [ArgumentsProvider(nameof(GetBuildMatrix), TestArgumentsEntryProviderMethodName = nameof(FormatBuildMatrixEntry))] + public async Task InvokeTestingPlatform_Target_Should_Build_Without_Warnings_And_Execute_Passing_Test_And_Pass_TheRun_SingleTfm(string testCommand, string tfm, BuildConfiguration compilationMode, bool testSucceeded) + => await InvokeTestingPlatform_Target_Should_Build_Without_Warnings_And_Execute_Passing_Test_And_Pass_TheRun_Detail(testCommand, tfm, false, [tfm], compilationMode, testSucceeded); + + [ArgumentsProvider(nameof(GetBuildMatrixMultiTfm), TestArgumentsEntryProviderMethodName = nameof(FormatBuildMatrixEntry))] + public async Task InvokeTestingPlatform_Target_Should_Build_Without_Warnings_And_Execute_Passing_Test_And_Pass_TheRun_MultiTfm(string testCommand, string multiTfm, BuildConfiguration compilationMode, bool testSucceeded) + => await InvokeTestingPlatform_Target_Should_Build_Without_Warnings_And_Execute_Passing_Test_And_Pass_TheRun_Detail(testCommand, multiTfm, true, TargetFrameworks.All.Select(x => x.Arguments).ToArray(), compilationMode, testSucceeded); + + private async Task InvokeTestingPlatform_Target_Should_Build_Without_Warnings_And_Execute_Passing_Test_And_Pass_TheRun_Detail(string testCommand, string tfm, bool isMultiTfm, string[] tfmsToAssert, BuildConfiguration compilationMode, bool testSucceeded) + => await RetryHelper.RetryAsync( + async () => + { + using TestAsset testAsset = await TestAsset.GenerateAssetAsync( + AssetName, + SourceCode + .PatchCodeWithReplace("$PlatformTarget$", "x64") + .PatchCodeWithReplace("$TargetFrameworks$", isMultiTfm ? $"{tfm}" : $"{tfm}") + .PatchCodeWithReplace("$AssertValue$", testSucceeded.ToString().ToLowerInvariant()) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); + string testResultFolder = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N")); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{testCommand} -p:TestingPlatformCommandLineArguments=\"--treenode-filter /*/*/*/TestMethod1 --results-directory %22{testResultFolder}%22\" -p:Configuration={compilationMode} -p:nodeReuse=false -bl:{binlogFile} /warnAsError \"{testAsset.TargetAssetPath}\"", _acceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: true); + + foreach (string tfmToAssert in tfmsToAssert) + { + CommonAssert(compilationResult, tfmToAssert, testSucceeded, testResultFolder); + } + }, 3, TimeSpan.FromSeconds(10)); + + [NonTest] // TODO: AMAURY/MARCO - Investigate this test + public async Task Invoke_DotnetTest_With_Arch_Switch_x86_Should_Work() + => await RetryHelper.RetryAsync( + async () => + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + + string root = RootFinder.Find(); + string x86Muxer = Path.Combine(root, ".dotnet", "x86"); + var dotnetRootX86 = new Dictionary + { + { "DOTNET_ROOT_X86", x86Muxer }, + }; + + TestAsset testAsset = await TestAsset.GenerateAssetAsync( + AssetName, + SourceCode + .PatchCodeWithReplace("$PlatformTarget$", string.Empty) + .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent.Arguments}") + .PatchCodeWithReplace("$AssertValue$", bool.TrueString.ToLowerInvariant()) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); + string testResultFolder = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N")); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test --arch x86 -p:TestingPlatformDotnetTestSupport=True -p:Configuration=Release -p:nodeReuse=false -bl:{binlogFile} /warnAsError \"{testAsset.TargetAssetPath}\"", _acceptanceFixture.NuGetGlobalPackagesFolder.Path, + environmentVariables: dotnetRootX86, + failIfReturnValueIsNotZero: false); + + string outputFileLog = Directory.GetFiles(testAsset.TargetAssetPath, "MSBuild Tests_net8.0_x86.log", SearchOption.AllDirectories).Single(); + Assert.IsTrue(File.Exists(outputFileLog), $"Expected file '{outputFileLog}'"); + string logFileContent = File.ReadAllText(outputFileLog); + Assert.IsTrue(Regex.IsMatch(logFileContent, ".*win-x86.*"), logFileContent); + Assert.IsTrue(Regex.IsMatch(logFileContent, @".*dotnet\.exe run.*"), logFileContent); + Assert.IsTrue(Regex.IsMatch(logFileContent, @".*--arch x86.*"), logFileContent); + }, 3, TimeSpan.FromSeconds(10)); + + private static void CommonAssert(DotnetMuxerResult compilationResult, string tfm, bool testSucceeded, string testResultFolder) + { + Assert.IsTrue(Regex.IsMatch(compilationResult.StandardOutput, $".*Run tests:.* \\[{tfm}|x64\\]"), compilationResult.StandardOutput); + if (testSucceeded) + { + Assert.IsTrue(Regex.IsMatch(compilationResult.StandardOutput, $".*error : Tests failed:.* \\[{tfm}|x64\\]"), compilationResult.StandardOutput); + } + else + { + Assert.IsTrue(Regex.IsMatch(compilationResult.StandardOutput, $"Tests succeeded:.* \\[{tfm}|x64\\]"), compilationResult.StandardOutput); + } + + string outputFileLog = Path.Combine(testResultFolder, $"MSBuild Tests_{tfm}_x64.log"); + Assert.IsTrue(File.Exists(outputFileLog), $"Expected file '{outputFileLog}'"); + Assert.IsFalse(string.IsNullOrEmpty(File.ReadAllText(outputFileLog)), $"Content of file '{File.ReadAllText(outputFileLog)}'"); + } + + // We avoid to test the multitfm because it's already tested with the above tests and we don't want to have too heavy testing, msbuild is pretty heavy(a lot of processes started + // due to the no nodereuse and makes tests flaky. + // We test two functionality for the same reason, we don't want to load too much the CI only for UX reasons. + [ArgumentsProvider(nameof(GetBuildMatrix), TestArgumentsEntryProviderMethodName = nameof(FormatBuildMatrixEntry))] + public async Task InvokeTestingPlatform_Target_Showing_Error_And_Do_Not_Capture_The_Output_SingleTfm(string testCommand, string tfm, BuildConfiguration compilationMode, bool testSucceeded) + { + // We test only failed but we don't want to have too much argument provider overload. + if (testSucceeded) + { + return; + } + + using TestAsset testAsset = await TestAsset.GenerateAssetAsync( + AssetName, + SourceCode + .PatchCodeWithReplace("$PlatformTarget$", "x64") + .PatchCodeWithReplace("$TargetFrameworks$", $"{tfm}") + .PatchCodeWithReplace("$AssertValue$", testSucceeded.ToString().ToLowerInvariant()) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{testCommand} -p:TestingPlatformShowTestsFailure=True -p:TestingPlatformCaptureOutput=False -p:Configuration={compilationMode} -p:nodeReuse=false -bl:{binlogFile} {testAsset.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); + Assert.Contains("error test failed: TestMethod2 (", compilationResult.StandardOutput); + Assert.Contains("Assert.IsTrue: Expected 'true', but got 'false'.", compilationResult.StandardOutput); + Assert.Contains(".NET Testing Platform", compilationResult.StandardOutput); + } + + private const string SourceCode = """ +#file MSBuild Tests.csproj + + + $TargetFrameworks$ + $PlatformTarget$ + enable + enable + Exe + true + preview + false + MSBuildTests + true + + + + + + + + + + + +#file Program.cs +using MSBuildTests; +ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); +builder.AddTestFramework(new MSBuild_Tests.SourceGeneratedTestNodesBuilder()); +builder.AddMSBuild(); +using ITestApplication app = await builder.BuildAsync(); +return await app.RunAsync(); + +#file UnitTest1.cs +namespace MSBuildTests; + +[TestGroup] +public class UnitTest1 +{ + public void TestMethod1() + { + Assert.IsTrue(true); + } + + public void TestMethod2() + { + Assert.IsTrue($AssertValue$); + } +} + +#file Usings.cs +global using Microsoft.Testing.Platform.Builder; +global using Microsoft.Testing.Internal.Framework; +global using Microsoft.Testing.Platform.MSBuild; +"""; +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSTestRunnerTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSTestRunnerTests.cs deleted file mode 100644 index 8aca13687a..0000000000 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSTestRunnerTests.cs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Build.Logging.StructuredLogger; -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; - -using SL = Microsoft.Build.Logging.StructuredLogger; -using SystemTask = System.Threading.Tasks.Task; - -namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; - -[TestGroup] -public class MSTestRunnerTests : AcceptanceTestBase -{ - private static readonly SemaphoreSlim Lock = new(1); - private readonly AcceptanceFixture _acceptanceFixture; - private const string AssetName = "MSTestProject"; - - public MSTestRunnerTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) - { - _acceptanceFixture = acceptanceFixture; - } - - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] - public async SystemTask EnableMSTestRunner_True_Will_Run_Standalone(string tfm, BuildConfiguration buildConfiguration, Verb verb) - { - await Lock.WaitAsync(); - try - { - using TestAsset generator = await TestAsset.GenerateAssetAsync( - AssetName, - CurrentMSTestSourceCode - .PatchCodeWithReplace("$TargetFramework$", $"{tfm}") - .PatchCodeWithReplace("$MicrosoftNETTestSdkVersion$", MicrosoftNETTestSdkVersion) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) - .PatchCodeWithReplace("$EnableMSTestRunner$", "true") - .PatchCodeWithReplace("$OutputType$", "Exe") - .PatchCodeWithReplace("$Extra$", string.Empty), - addPublicFeeds: true); - string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - compilationResult = await DotnetCli.RunAsync( - $"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -bl:{binlogFile} -r {RID}", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - - SL.Build binLog = SL.Serialization.Read(binlogFile); - Assert.IsNotEmpty(binLog.FindChildrenRecursive() - .Where(x => x.Title.Contains("ProjectCapability")) - .Where(x => x.Children.Any(c => ((Item)c).Name == "TestingPlatformServer"))); - - var testHost = TestInfrastructure.TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration, verb: verb); - TestHostResult testHostResult = await testHost.ExecuteAsync(); - testHostResult.AssertOutputContains("Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1"); - } - finally - { - Lock.Release(); - } - } - - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] - public async SystemTask EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string tfm, BuildConfiguration buildConfiguration, Verb verb) - { - await Lock.WaitAsync(); - try - { - using TestAsset generator = await TestAsset.GenerateAssetAsync( - AssetName, - (CurrentMSTestSourceCode + """ -#file Program.cs - -using Microsoft.Testing.Platform.Builder; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddMSTest(() => new[] { typeof(Program).Assembly }); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); -""") - .PatchCodeWithReplace("$TargetFramework$", $"{tfm}") - .PatchCodeWithReplace("$MicrosoftNETTestSdkVersion$", MicrosoftNETTestSdkVersion) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) - .PatchCodeWithReplace("$EnableMSTestRunner$", "true") - .PatchCodeWithReplace("$OutputType$", "Exe") - .PatchCodeWithReplace("$Extra$", """ -False -preview -"""), - addPublicFeeds: true); - string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - compilationResult = await DotnetCli.RunAsync( - $"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -bl:{binlogFile} -r {RID}", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - var testHost = TestInfrastructure.TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration, verb: verb); - TestHostResult testHostResult = await testHost.ExecuteAsync(); - testHostResult.AssertOutputContains("Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1"); - } - finally - { - Lock.Release(); - } - } - - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] - public async SystemTask EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string tfm, BuildConfiguration buildConfiguration, Verb verb) - { - await Lock.WaitAsync(); - try - { - using TestAsset generator = await TestAsset.GenerateAssetAsync( - AssetName, - CurrentMSTestSourceCode - .PatchCodeWithReplace("$TargetFramework$", $"{tfm}") - .PatchCodeWithReplace("$MicrosoftNETTestSdkVersion$", MicrosoftNETTestSdkVersion) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) - .PatchCodeWithReplace("$EnableMSTestRunner$", "false") - .PatchCodeWithReplace("$OutputType$", "Exe") - .PatchCodeWithReplace("$Extra$", string.Empty), - addPublicFeeds: true); - string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - try - { - compilationResult = await DotnetCli.RunAsync($"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -bl:{binlogFile} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - var testHost = TestInfrastructure.TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration, verb: verb); - TestHostResult testHostResult = await testHost.ExecuteAsync(); - Assert.AreEqual(string.Empty, testHostResult.StandardOutput); - } - catch (Exception ex) - { - if (TargetFrameworks.NetFramework.Any(x => x.Arguments == tfm)) - { - Assert.IsTrue(ex.Message.Contains("Program does not contain a static 'Main' method suitable for an entry point"), ex.Message); - - // .NET Framework does not insert the entry point for empty program. - return; - } - } - } - finally - { - Lock.Release(); - } - } - - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] - public async SystemTask EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string tfm, BuildConfiguration buildConfiguration, Verb verb) - { - await Lock.WaitAsync(); - try - { - using TestAsset generator = await TestAsset.GenerateAssetAsync( - AssetName, - CurrentMSTestSourceCode - .PatchCodeWithReplace("$TargetFramework$", $"{tfm}") - .PatchCodeWithReplace("$MicrosoftNETTestSdkVersion$", MicrosoftNETTestSdkVersion) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) - .PatchCodeWithReplace("$EnableMSTestRunner$", string.Empty) - .PatchCodeWithReplace("$OutputType$", string.Empty) - .PatchCodeWithReplace("$Extra$", string.Empty), - addPublicFeeds: true); - - string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - compilationResult = await DotnetCli.RunAsync($"{verb} -bl:{binlogFile} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -r {RID} ", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - - SL.Build binLog = SL.Serialization.Read(binlogFile); - Assert.IsEmpty(binLog.FindChildrenRecursive() - .Where(x => x.Title.Contains("ProjectCapability")) - .Where(x => x.Children.Any(c => ((Item)c).Name == "TestingPlatformServer"))); - } - finally - { - Lock.Release(); - } - } -} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj index cde08de443..b010143aa8 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj @@ -2,68 +2,40 @@ $(NetCurrent) - false - true - enable - false $(TestRunnerAdditionalArguments) --retry-failed-tests 3 - Exe + false + $(DefineConstants);SKIP_INTERMEDIATE_TARGET_FRAMEWORKS - - - - - - - - - - - - - - - + + - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - + + + + - - - - - - - - - + + diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Program.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Program.cs index 3bce740352..f78a15125c 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Program.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Program.cs @@ -2,13 +2,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Diagnostics; -using System.Runtime.InteropServices; using Microsoft.Testing.Extensions; using Microsoft.Testing.Internal.Framework.Configurations; using Microsoft.Testing.Platform.Acceptance.IntegrationTests; -using Microsoft.Testing.Platform.CommandLine; -using Microsoft.Testing.Platform.Extensions.TestHost; // Opt-out telemetry Environment.SetEnvironmentVariable("DOTNET_CLI_TELEMETRY_OPTOUT", "1"); @@ -17,13 +14,13 @@ DotnetCli.DoNotRetry = Debugger.IsAttached; ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.TestHost.AddTestApplicationLifecycleCallbacks(sp => new GlobalTasks(sp.GetCommandLineOptions())); builder.AddTestFramework(new TestFrameworkConfiguration(Debugger.IsAttached ? 1 : Environment.ProcessorCount), new SourceGeneratedTestNodesBuilder()); #if ENABLE_CODECOVERAGE builder.AddCodeCoverageProvider(); #endif -builder.AddCrashDumpProvider(); +builder.AddHangDumpProvider(); +builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddTrxReportProvider(); builder.AddRetryProvider(); @@ -36,51 +33,3 @@ CompositeExtensionFactory slowestTestCompositeServiceFacto int returnValue = await app.RunAsync(); Console.WriteLine($"Process started: {CommandLine.TotalProcessesAttempt}"); return returnValue; - -internal sealed class GlobalTasks : ITestApplicationLifecycleCallbacks -{ - private readonly ICommandLineOptions _commandLineOptions; - - public GlobalTasks(ICommandLineOptions commandLineOptions) - { - _commandLineOptions = commandLineOptions; - } - - public string Uid => nameof(GlobalTasks); - - public string Version => "1.0.0"; - - public string DisplayName => string.Empty; - - public string Description => string.Empty; - - public Task IsEnabledAsync() => Task.FromResult(true); - - public async Task AfterRunAsync(int returnValue, CancellationToken cancellationToken) - { - // Remove net462 tests from baseline on non-Windows - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - string[] allLines = File.ReadAllLines(Path.Combine(AppContext.BaseDirectory, "testsbaseline.txt")); - using FileStream fs = File.OpenWrite(Path.Combine(AppContext.BaseDirectory, "testsbaseline.notwin.txt")); - using StreamWriter sw = new(fs); - foreach (string line in allLines.Where(x => !x.Contains("net462"))) - { - await sw.WriteLineAsync(line); - } - } - - // Verify run tests are matching expected baseline - TestsRunWatchDog.BaselineFile = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? Path.Combine(AppContext.BaseDirectory, "testsbaseline.txt") - : Path.Combine(AppContext.BaseDirectory, "testsbaseline.notwin.txt"); - - await TestsRunWatchDog.VerifyAsync(skip: _commandLineOptions.IsServerMode(), fixBaseLine: true); - } - - public Task BeforeRunAsync(CancellationToken cancellationToken) - { - Console.WriteLine($"Parallelism: '{(Debugger.IsAttached ? 1 : Environment.ProcessorCount)}'"); - return Task.CompletedTask; - } -} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs index 297f2c45bf..ac82c6a99f 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs @@ -35,12 +35,12 @@ public async Task Telemetry_ByDefault_TelemetryIsEnabled(string tfm) string diagContentsPattern = """ -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION TestApplicationOptions.EnableTelemetry: True -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION TESTINGPLATFORM_TELEMETRY_OPTOUT environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION DOTNET_CLI_TELEMETRY_OPTOUT environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION TESTINGPLATFORM_NOBANNER environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION DOTNET_NOLOGO environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION Telemetry is 'ENABLED' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TestApplicationOptions.EnableTelemetry: True +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_TELEMETRY_OPTOUT environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_CLI_TELEMETRY_OPTOUT environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_NOBANNER environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_NOLOGO environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG Telemetry is 'ENABLED' """; await AssertDiagnosticReportAsync(testHostResult, diagPathPattern, diagContentsPattern); } @@ -64,12 +64,12 @@ public async Task Telemetry_WhenOptingOutTelemetry_WithEnvironmentVariable_Telem string diagContentsPattern = """ -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION TestApplicationOptions.EnableTelemetry: True -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION TESTINGPLATFORM_TELEMETRY_OPTOUT environment variable: '1' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION DOTNET_CLI_TELEMETRY_OPTOUT environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION TESTINGPLATFORM_NOBANNER environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION DOTNET_NOLOGO environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION Telemetry is 'DISABLED' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TestApplicationOptions.EnableTelemetry: True +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_TELEMETRY_OPTOUT environment variable: '1' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_CLI_TELEMETRY_OPTOUT environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_NOBANNER environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_NOLOGO environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG Telemetry is 'DISABLED' """; await AssertDiagnosticReportAsync(testHostResult, diagPathPattern, diagContentsPattern); } @@ -93,12 +93,12 @@ public async Task Telemetry_WhenOptingOutTelemetry_With_DOTNET_CLI_EnvironmentVa string diagContentsPattern = """ -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION TestApplicationOptions.EnableTelemetry: True -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION TESTINGPLATFORM_TELEMETRY_OPTOUT environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION DOTNET_CLI_TELEMETRY_OPTOUT environment variable: '1' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION TESTINGPLATFORM_NOBANNER environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION DOTNET_NOLOGO environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION Telemetry is 'DISABLED' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TestApplicationOptions.EnableTelemetry: True +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_TELEMETRY_OPTOUT environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_CLI_TELEMETRY_OPTOUT environment variable: '1' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_NOBANNER environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_NOLOGO environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG Telemetry is 'DISABLED' """; await AssertDiagnosticReportAsync(testHostResult, diagPathPattern, diagContentsPattern); } @@ -116,12 +116,12 @@ public async Task Telemetry_WhenEnableTelemetryIsFalse_WithTestApplicationOption string diagContentsPattern = """ -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION TestApplicationOptions.EnableTelemetry: False -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION TESTINGPLATFORM_TELEMETRY_OPTOUT environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION DOTNET_CLI_TELEMETRY_OPTOUT environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION TESTINGPLATFORM_NOBANNER environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION DOTNET_NOLOGO environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager INFORMATION Telemetry is 'DISABLED' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TestApplicationOptions.EnableTelemetry: False +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_TELEMETRY_OPTOUT environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_CLI_TELEMETRY_OPTOUT environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_NOBANNER environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_NOLOGO environment variable: '' +.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG Telemetry is 'DISABLED' """; await AssertDiagnosticReportAsync(testHostResult, diagPathPattern, diagContentsPattern); } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs index d296dfc91a..3155fbc4f5 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs @@ -61,7 +61,7 @@ public async Task Trx_WhenTestHostCrash_ErrorIsDisplayedInsideTheTrx(string tfm) string trxFile = Directory.GetFiles(testHost.DirectoryName, $"{fileName}.trx", SearchOption.AllDirectories).Single(); string trxContent = File.ReadAllText(trxFile); Assert.That(Regex.IsMatch(trxContent, @"Test host process pid: .* crashed\."), trxContent); - Assert.That(trxContent.Contains(@""), trxContent); + Assert.That(trxContent.Contains(""""""), trxContent); } [ArgumentsProvider(nameof(TargetFrameworks.Net), typeof(TargetFrameworks))] @@ -71,7 +71,7 @@ public async Task Trx_WhenSkipTest_ItAppearsAsExpectedInsideTheTrx(string tfm) var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPathWithSkippedTest, TestAssetFixture.AssetNameUsingMSTest, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {fileName}.trx"); - testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); string trxFile = Directory.GetFiles(testHost.DirectoryName, $"{fileName}.trx", SearchOption.AllDirectories).Single(); @@ -83,8 +83,8 @@ public async Task Trx_WhenSkipTest_ItAppearsAsExpectedInsideTheTrx(string tfm) Assert.That(trxContent.Contains(@""), trxContent); - Assert.That(trxContent.Contains(@""), trxContent); + Assert.That(trxContent.Contains(""""""), trxContent); + Assert.That(trxContent.Contains(""""""), trxContent); } [ArgumentsProvider(nameof(TargetFrameworks.Net), typeof(TargetFrameworks))] @@ -107,7 +107,7 @@ public async Task Trx_UsingDataDriven_CreatesUnitTestTagForEachOneInsideTheTrx(s var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPathWithSkippedTest, TestAssetFixture.AssetNameUsingMSTest, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {fileName}.trx"); - testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); string trxFile = Directory.GetFiles(testHost.DirectoryName, $"{fileName}.trx", SearchOption.AllDirectories).Single(); @@ -212,11 +212,13 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test preview + + + - - - - + + + @@ -266,13 +268,8 @@ public void TestMethod1() true - - + - - - - @@ -318,19 +315,17 @@ public void TestMethod1(string s) TestCode .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); yield return (WithSkippedTest, AssetNameUsingMSTest, MSTestCode .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) .PatchCodeWithReplace("$IgnoreTestAttributeOrNothing$", "[Ignore]")); yield return (WithDataRow, AssetNameUsingMSTest, MSTestCode .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingPlatformExtensionsVersion$", MicrosoftTestingPlatformExtensionsVersion) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) .PatchCodeWithReplace("$IgnoreTestAttributeOrNothing$", string.Empty)); } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/testsbaseline.txt b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/testsbaseline.txt deleted file mode 100644 index ccee029868..0000000000 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/testsbaseline.txt +++ /dev/null @@ -1,333 +0,0 @@ -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.AbortionTests.AbortWithCTRLPlusC_TestHost_Succeeded(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.AbortionTests.AbortWithCTRLPlusC_TestHost_Succeeded(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.AbortionTests.AbortWithCTRLPlusC_TestHost_Succeeded(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.AbortionTests.AbortWithCTRLPlusC_TestHost_Succeeded(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_CustomPrefix_Succeeded(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_CustomPrefix_Succeeded(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_CustomPrefix_Succeeded(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_CustomPrefix_Succeeded(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_Disable_Succeeded(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_Disable_Succeeded(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_Disable_Succeeded(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_Disable_Succeeded(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_Succeeded(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_Succeeded(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_Succeeded(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_Succeeded(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_SynchronousWrite_Succeeded(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_SynchronousWrite_Succeeded(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_SynchronousWrite_Succeeded(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_SynchronousWrite_Succeeded(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_Verbosity_Succeeded(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_Verbosity_Succeeded(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_Verbosity_Succeeded(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_EnableWithEnvironmentVariables_Verbosity_Succeeded(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticAndOutputDirectoryAreSpecified_ReportIsGeneratedInSpecifiedLocation(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticAndOutputDirectoryAreSpecified_ReportIsGeneratedInSpecifiedLocation(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticAndOutputDirectoryAreSpecified_ReportIsGeneratedInSpecifiedLocation(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticAndOutputDirectoryAreSpecified_ReportIsGeneratedInSpecifiedLocation(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticAndOutputFilePrefixAndOutputDirectoryAreSpecified_ReportIsGeneratedInSpecifiedLocation(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticAndOutputFilePrefixAndOutputDirectoryAreSpecified_ReportIsGeneratedInSpecifiedLocation(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticAndOutputFilePrefixAndOutputDirectoryAreSpecified_ReportIsGeneratedInSpecifiedLocation(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticAndOutputFilePrefixAndOutputDirectoryAreSpecified_ReportIsGeneratedInSpecifiedLocation(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticAndOutputFilePrefixAreSpecified_ReportIsGeneratedInDefaultLocation(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticAndOutputFilePrefixAreSpecified_ReportIsGeneratedInDefaultLocation(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticAndOutputFilePrefixAreSpecified_ReportIsGeneratedInDefaultLocation(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticAndOutputFilePrefixAreSpecified_ReportIsGeneratedInDefaultLocation(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticFilePrefixAndDiagnosticOutputDirectoryButNotDiagnosticAreSpecified_ReportGenerationFails(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticFilePrefixAndDiagnosticOutputDirectoryButNotDiagnosticAreSpecified_ReportGenerationFails(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticFilePrefixAndDiagnosticOutputDirectoryButNotDiagnosticAreSpecified_ReportGenerationFails(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticFilePrefixAndDiagnosticOutputDirectoryButNotDiagnosticAreSpecified_ReportGenerationFails(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticIsSpecified_ReportIsGeneratedInDefaultLocation(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticIsSpecified_ReportIsGeneratedInDefaultLocation(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticIsSpecified_ReportIsGeneratedInDefaultLocation(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticIsSpecified_ReportIsGeneratedInDefaultLocation(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticOutputDirectoryButNotDiagnosticIsSpecified_ReportGenerationFails(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticOutputDirectoryButNotDiagnosticIsSpecified_ReportGenerationFails(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticOutputDirectoryButNotDiagnosticIsSpecified_ReportGenerationFails(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticOutputDirectoryButNotDiagnosticIsSpecified_ReportGenerationFails(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticOutputFilePrefixButNotDiagnosticIsSpecified_ReportGenerationFails(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticOutputFilePrefixButNotDiagnosticIsSpecified_ReportGenerationFails(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticOutputFilePrefixButNotDiagnosticIsSpecified_ReportGenerationFails(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DiagnosticTests.Diag_WhenDiagnosticOutputFilePrefixButNotDiagnosticIsSpecified_ReportGenerationFails(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DotnetTestCliTests.DotnetTest_Should_Execute_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (net462,Debug) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DotnetTestCliTests.DotnetTest_Should_Execute_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (net462,Release) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DotnetTestCliTests.DotnetTest_Should_Execute_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (net6.0,Debug) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DotnetTestCliTests.DotnetTest_Should_Execute_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (net6.0,Release) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DotnetTestCliTests.DotnetTest_Should_Execute_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (net7.0,Debug) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DotnetTestCliTests.DotnetTest_Should_Execute_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (net7.0,Release) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DotnetTestCliTests.DotnetTest_Should_Execute_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (net8.0,Debug) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.DotnetTestCliTests.DotnetTest_Should_Execute_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration) (net8.0,Release) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.EnvironmentVariablesConfigurationProviderTests.SetEnvironmentVariable_ShouldSucceed(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.EnvironmentVariablesConfigurationProviderTests.SetEnvironmentVariable_ShouldSucceed(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.EnvironmentVariablesConfigurationProviderTests.SetEnvironmentVariable_ShouldSucceed(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.EnvironmentVariablesConfigurationProviderTests.SetEnvironmentVariable_ShouldSucceed(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_Honor_Request_Complete(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_Honor_Request_Complete(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_Honor_Request_Complete(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_Honor_Request_Complete(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenFilterIsSpecified_OnlyFilteredTestsAreRun(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenFilterIsSpecified_OnlyFilteredTestsAreRun(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenFilterIsSpecified_OnlyFilteredTestsAreRun(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenFilterIsSpecified_OnlyFilteredTestsAreRun(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenListTestsAndFilterAreSpecified_OnlyFilteredTestsAreFound(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenListTestsAndFilterAreSpecified_OnlyFilteredTestsAreFound(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenListTestsAndFilterAreSpecified_OnlyFilteredTestsAreFound(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenListTestsAndFilterAreSpecified_OnlyFilteredTestsAreFound(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenListTestsAndMinimumExpectedTestsAreSpecified_DiscoveryFails(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenListTestsAndMinimumExpectedTestsAreSpecified_DiscoveryFails(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenListTestsAndMinimumExpectedTestsAreSpecified_DiscoveryFails(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenListTestsAndMinimumExpectedTestsAreSpecified_DiscoveryFails(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenListTestsIsSpecified_AllTestsAreFound(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenListTestsIsSpecified_AllTestsAreFound(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenListTestsIsSpecified_AllTestsAreFound(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenListTestsIsSpecified_AllTestsAreFound(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenMinimumExpectedTestsIsSpecifiedAndEnoughTestsRun_ResultIsOk(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenMinimumExpectedTestsIsSpecifiedAndEnoughTestsRun_ResultIsOk(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenMinimumExpectedTestsIsSpecifiedAndEnoughTestsRun_ResultIsOk(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenMinimumExpectedTestsIsSpecifiedAndEnoughTestsRun_ResultIsOk(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenMinimumExpectedTestsIsSpecifiedAndNotEnoughTestsRun_ResultIsNotOk(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenMinimumExpectedTestsIsSpecifiedAndNotEnoughTestsRun_ResultIsNotOk(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenMinimumExpectedTestsIsSpecifiedAndNotEnoughTestsRun_ResultIsNotOk(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenMinimumExpectedTestsIsSpecifiedAndNotEnoughTestsRun_ResultIsNotOk(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenOnlyAssetNameIsSpecified_AllTestsAreRun(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenOnlyAssetNameIsSpecified_AllTestsAreRun(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenOnlyAssetNameIsSpecified_AllTestsAreRun(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExecutionTests.Exec_WhenOnlyAssetNameIsSpecified_AllTestsAreRun(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExitOnProcessExitTests.ExitOnProcessExit_Succeed(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExitOnProcessExitTests.ExitOnProcessExit_Succeed(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExitOnProcessExitTests.ExitOnProcessExit_Succeed(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.ExitOnProcessExitTests.ExitOnProcessExit_Succeed(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.HelpTests.Help_WhenMSTestExtensionRegistered_OutputHelpContentOfRegisteredExtension(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.HelpTests.Help_WhenMSTestExtensionRegistered_OutputHelpContentOfRegisteredExtension(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.HelpTests.Help_WhenMSTestExtensionRegistered_OutputHelpContentOfRegisteredExtension(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.HelpTests.Help_WhenMSTestExtensionRegistered_OutputHelpContentOfRegisteredExtension(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.HelpTests.Help_WhenNoExtensionRegistered_OutputDefaultHelpContent(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.HelpTests.Help_WhenNoExtensionRegistered_OutputDefaultHelpContent(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.HelpTests.Help_WhenNoExtensionRegistered_OutputDefaultHelpContent(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.HelpTests.Help_WhenNoExtensionRegistered_OutputDefaultHelpContent(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.HelpTests.Help_WhenNoExtensionRegisteredAndUnknownOptionIsSpecified_OutputDefaultHelpContentAndUnknownOption(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.HelpTests.Help_WhenNoExtensionRegisteredAndUnknownOptionIsSpecified_OutputDefaultHelpContentAndUnknownOption(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.HelpTests.Help_WhenNoExtensionRegisteredAndUnknownOptionIsSpecified_OutputDefaultHelpContentAndUnknownOption(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.HelpTests.Help_WhenNoExtensionRegisteredAndUnknownOptionIsSpecified_OutputDefaultHelpContentAndUnknownOption(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net462,Debug,CommandLine) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net462,Debug,EnvironmentVariable) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net462,Release,CommandLine) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net462,Release,EnvironmentVariable) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net6.0,Debug,CommandLine) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net6.0,Debug,EnvironmentVariable) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net6.0,Release,CommandLine) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net6.0,Release,EnvironmentVariable) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net7.0,Debug,CommandLine) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net7.0,Debug,EnvironmentVariable) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net7.0,Release,CommandLine) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net7.0,Release,EnvironmentVariable) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net8.0,Debug,CommandLine) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net8.0,Debug,EnvironmentVariable) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net8.0,Release,CommandLine) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.IgnoreExitCodeTests.If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, string, string) (net8.0,Release,EnvironmentVariable) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.InfoTests.Info_WhenMSTestExtensionRegistered_OutputInfoContentOfRegisteredExtension(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.InfoTests.Info_WhenMSTestExtensionRegistered_OutputInfoContentOfRegisteredExtension(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.InfoTests.Info_WhenMSTestExtensionRegistered_OutputInfoContentOfRegisteredExtension(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.InfoTests.Info_WhenMSTestExtensionRegistered_OutputInfoContentOfRegisteredExtension(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.InfoTests.Info_WhenNoExtensionRegistered_OutputDefaultInfoContent(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.InfoTests.Info_WhenNoExtensionRegistered_OutputDefaultInfoContent(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.InfoTests.Info_WhenNoExtensionRegistered_OutputDefaultInfoContent(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.InfoTests.Info_WhenNoExtensionRegistered_OutputDefaultInfoContent(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (multitfm,Debug,build --no-restore /t:Test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (multitfm,Debug,test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (multitfm,Release,build --no-restore /t:Test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (multitfm,Release,test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net462,Debug,build --no-restore /t:Test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net462,Debug,test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net462,Release,build --no-restore /t:Test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net462,Release,test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net6.0,Debug,build --no-restore /t:Test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net6.0,Debug,test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net6.0,Release,build --no-restore /t:Test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net6.0,Release,test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net7.0,Debug,build --no-restore /t:Test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net7.0,Debug,test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net7.0,Release,build --no-restore /t:Test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net7.0,Release,test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net8.0,Debug,build --no-restore /t:Test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net8.0,Debug,test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net8.0,Release,build --no-restore /t:Test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSBuildMSTestRunnerTests.MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, bool, string) (net8.0,Release,test) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net462,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net6.0,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net7.0,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Debug,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Debug,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Release,build) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.MSTestRunnerTests.EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string, Microsoft.Testing.TestInfrastructure.BuildConfiguration, Microsoft.Testing.TestInfrastructure.Verb) (net8.0,Release,publish) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.UsingNoBanner_InTheEnvironmentVars_TheBannerDoesNotAppear(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.UsingNoBanner_InTheEnvironmentVars_TheBannerDoesNotAppear(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.UsingNoBanner_InTheEnvironmentVars_TheBannerDoesNotAppear(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.UsingNoBanner_InTheEnvironmentVars_TheBannerDoesNotAppear(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.UsingNoBanner_TheBannerDoesNotAppear(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.UsingNoBanner_TheBannerDoesNotAppear(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.UsingNoBanner_TheBannerDoesNotAppear(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.UsingNoBanner_TheBannerDoesNotAppear(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.WithoutUsingNoBanner_TheBannerAppears(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.WithoutUsingNoBanner_TheBannerAppears(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.WithoutUsingNoBanner_TheBannerAppears(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.NoBannerTests.WithoutUsingNoBanner_TheBannerAppears(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_ByDefault_TelemetryIsEnabled(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_ByDefault_TelemetryIsEnabled(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_ByDefault_TelemetryIsEnabled(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_ByDefault_TelemetryIsEnabled(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_WhenEnableTelemetryIsFalse_WithTestApplicationOptions_TelemetryIsDisabled(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_WhenEnableTelemetryIsFalse_WithTestApplicationOptions_TelemetryIsDisabled(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_WhenEnableTelemetryIsFalse_WithTestApplicationOptions_TelemetryIsDisabled(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_WhenEnableTelemetryIsFalse_WithTestApplicationOptions_TelemetryIsDisabled(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_WhenOptingOutTelemetry_With_DOTNET_CLI_EnvironmentVariable_TelemetryIsDisabled(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_WhenOptingOutTelemetry_With_DOTNET_CLI_EnvironmentVariable_TelemetryIsDisabled(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_WhenOptingOutTelemetry_With_DOTNET_CLI_EnvironmentVariable_TelemetryIsDisabled(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_WhenOptingOutTelemetry_With_DOTNET_CLI_EnvironmentVariable_TelemetryIsDisabled(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_WhenOptingOutTelemetry_WithEnvironmentVariable_TelemetryIsDisabled(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_WhenOptingOutTelemetry_WithEnvironmentVariable_TelemetryIsDisabled(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_WhenOptingOutTelemetry_WithEnvironmentVariable_TelemetryIsDisabled(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TelemetryTests.Telemetry_WhenOptingOutTelemetry_WithEnvironmentVariable_TelemetryIsDisabled(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TestHostProcessLifetimeHandlerTests.All_Interface_Methods_ShouldBe_Invoked(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TestHostProcessLifetimeHandlerTests.All_Interface_Methods_ShouldBe_Invoked(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TestHostProcessLifetimeHandlerTests.All_Interface_Methods_ShouldBe_Invoked(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TestHostProcessLifetimeHandlerTests.All_Interface_Methods_ShouldBe_Invoked(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_UsingDataDriven_CreatesUnitTestTagForEachOneInsideTheTrx(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_UsingDataDriven_CreatesUnitTestTagForEachOneInsideTheTrx(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_UsingDataDriven_CreatesUnitTestTagForEachOneInsideTheTrx(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsNotSpecified_TrxReportIsNotGenerated(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsNotSpecified_TrxReportIsNotGenerated(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsNotSpecified_TrxReportIsNotGenerated(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsNotSpecified_TrxReportIsNotGenerated(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsNotSpecifiedAndReportTrxPathIsSpecified_ErrorIsDisplayed(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsNotSpecifiedAndReportTrxPathIsSpecified_ErrorIsDisplayed(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsNotSpecifiedAndReportTrxPathIsSpecified_ErrorIsDisplayed(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsNotSpecifiedAndReportTrxPathIsSpecified_ErrorIsDisplayed(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecified_TrxReportIsGeneratedInDefaultLocation(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecified_TrxReportIsGeneratedInDefaultLocation(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecified_TrxReportIsGeneratedInDefaultLocation(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecified_TrxReportIsGeneratedInDefaultLocation(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecifiedAndListTestsIsSpecified_ErrorIsDisplayed(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecifiedAndListTestsIsSpecified_ErrorIsDisplayed(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecifiedAndListTestsIsSpecified_ErrorIsDisplayed(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecifiedAndListTestsIsSpecified_ErrorIsDisplayed(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecifiedWithFullPath_TrxReportShouldFail(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecifiedWithFullPath_TrxReportShouldFail(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecifiedWithFullPath_TrxReportShouldFail(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecifiedWithFullPath_TrxReportShouldFail(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecifiedWithRelativePath_TrxReportShouldFail(string) (net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecifiedWithRelativePath_TrxReportShouldFail(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecifiedWithRelativePath_TrxReportShouldFail(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenReportTrxIsSpecifiedWithRelativePath_TrxReportShouldFail(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenSkipTest_ItAppearsAsExpectedInsideTheTrx(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenSkipTest_ItAppearsAsExpectedInsideTheTrx(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenSkipTest_ItAppearsAsExpectedInsideTheTrx(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenTestHostCrash_ErrorIsDisplayedInsideTheTrx(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenTestHostCrash_ErrorIsDisplayedInsideTheTrx(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenTestHostCrash_ErrorIsDisplayedInsideTheTrx(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenTheTestNameHasInvalidXmlChar_TheTrxCreatedSuccessfully(string) (net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenTheTestNameHasInvalidXmlChar_TheTrxCreatedSuccessfully(string) (net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.TrxTests.Trx_WhenTheTestNameHasInvalidXmlChar_TheTrxCreatedSuccessfully(string) (net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Default - (net462)) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Default - (net6.0)) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Default - (net7.0)) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Default - (net8.0)) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Disabled - net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Disabled - net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Disabled - net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Disabled - net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (DisabledByEnvironmentVariable - net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (DisabledByEnvironmentVariable - net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (DisabledByEnvironmentVariable - net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (DisabledByEnvironmentVariable - net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Enabled - net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Enabled - net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Enabled - net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Enabled - net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (EnabledByEnvironmentVariable - net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (EnabledByEnvironmentVariable - net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (EnabledByEnvironmentVariable - net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (EnabledByEnvironmentVariable - net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Default - (net462)) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Default - (net6.0)) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Default - (net7.0)) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Default - (net8.0)) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Disabled - net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Disabled - net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Disabled - net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Disabled - net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (DisabledByEnvironmentVariable - net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (DisabledByEnvironmentVariable - net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (DisabledByEnvironmentVariable - net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (DisabledByEnvironmentVariable - net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Enabled - net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Enabled - net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Enabled - net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (Enabled - net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (EnabledByEnvironmentVariable - net462) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (EnabledByEnvironmentVariable - net6.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (EnabledByEnvironmentVariable - net7.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Microsoft.Testing.Platform.Acceptance.IntegrationTests.UnhandledExceptionPolicyTests.Mode, string) (EnabledByEnvironmentVariable - net8.0) -Microsoft.Testing.Platform.Acceptance.IntegrationTests.Microsoft.Testing.Platform.Acceptance.IntegrationTests.PublishAotNonNativeTests.RunTests_ThatEnablePublishAOT_ButDontBuildToNative() diff --git a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs index 519750db9c..b49d6d337b 100644 --- a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs +++ b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs @@ -25,18 +25,20 @@ public void ParentDomainShouldHonorSearchDirectoriesSpecifiedInRunsettings() { string sampleProjectDirPath = Path.GetDirectoryName(GetTestAssemblyPath("SampleProjectForAssemblyResolution")); string runSettingXml = - $@" + $""" + True - - - + + + - "; + + """; _testSourceHost = new TestSourceHost( GetTestAssemblyPath("DesktopTestProjectx86Debug"), @@ -54,18 +56,20 @@ public void ChildDomainResolutionPathsShouldHaveSearchDirectoriesSpecifiedInRuns string sampleProjectPath = GetTestAssemblyPath("SampleProjectForAssemblyResolution"); string sampleProjectDirPath = Path.GetDirectoryName(sampleProjectPath); string runSettingXml = - $@" - - False - - - - - - - - - "; + $""" + + + False + + + + + + + + + + """; _testSourceHost = new TestSourceHost( GetTestAssemblyPath("DesktopTestProjectx86Debug"), diff --git a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/ReflectionUtilityTests.cs b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/ReflectionUtilityTests.cs index 4fb9ea9b8d..a4799a6d26 100644 --- a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/ReflectionUtilityTests.cs +++ b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/ReflectionUtilityTests.cs @@ -19,12 +19,6 @@ public class ReflectionUtilityTests : TestContainer { private readonly Assembly _testAsset; - /// - /// Dictionary of Assemblies discovered to date. Must be locked as it may - /// be accessed in a multi-threaded context. - /// - private readonly Dictionary _resolvedAssemblies = []; - public ReflectionUtilityTests() { DirectoryInfo currentAssemblyDirectory = new FileInfo(typeof(ReflectionUtilityTests).Assembly.Location).Directory; @@ -42,7 +36,7 @@ public ReflectionUtilityTests() _testAsset = Assembly.ReflectionOnlyLoadFrom(testAssetPath); // This is needed for System assemblies. - AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(ReflectionOnlyOnResolve); + AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ReflectionOnlyOnResolve; } public void GetCustomAttributesShouldReturnAllAttributes() @@ -54,7 +48,7 @@ public void GetCustomAttributesShouldReturnAllAttributes() attributes.Should().NotBeNull(); attributes.Should().HaveCount(2); - string[] expectedAttributes = new string[] { "TestCategory : base", "Owner : base" }; + string[] expectedAttributes = ["TestCategory : base", "Owner : base"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -67,7 +61,7 @@ public void GetCustomAttributesShouldReturnAllAttributesIgnoringBaseInheritance( attributes.Should().NotBeNull(); attributes.Should().HaveCount(2); - string[] expectedAttributes = new string[] { "TestCategory : derived", "Owner : derived" }; + string[] expectedAttributes = ["TestCategory : derived", "Owner : derived"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -81,7 +75,7 @@ public void GetCustomAttributesShouldReturnAllAttributesWithBaseInheritance() attributes.Should().HaveCount(3); // Notice that the Owner on the base method does not show up since it can only be defined once. - string[] expectedAttributes = new string[] { "TestCategory : derived", "TestCategory : base", "Owner : derived" }; + string[] expectedAttributes = ["TestCategory : derived", "TestCategory : base", "Owner : derived"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -94,7 +88,7 @@ public void GetCustomAttributesOnTypeShouldReturnAllAttributes() attributes.Should().NotBeNull(); attributes.Should().HaveCount(1); - string[] expectedAttributes = new string[] { "TestCategory : ba" }; + string[] expectedAttributes = ["TestCategory : ba"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -107,7 +101,7 @@ public void GetCustomAttributesOnTypeShouldReturnAllAttributesIgnoringBaseInheri attributes.Should().NotBeNull(); attributes.Should().HaveCount(1); - string[] expectedAttributes = new string[] { "TestCategory : a" }; + string[] expectedAttributes = ["TestCategory : a"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -120,7 +114,7 @@ public void GetCustomAttributesOnTypeShouldReturnAllAttributesWithBaseInheritanc attributes.Should().NotBeNull(); attributes.Should().HaveCount(2); - string[] expectedAttributes = new string[] { "TestCategory : a", "TestCategory : ba" }; + string[] expectedAttributes = ["TestCategory : a", "TestCategory : ba"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -133,7 +127,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributes() attributes.Should().NotBeNull(); attributes.Should().HaveCount(1); - string[] expectedAttributes = new string[] { "TestCategory : base" }; + string[] expectedAttributes = ["TestCategory : base"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -146,7 +140,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributesIgnoringBaseInhe attributes.Should().NotBeNull(); attributes.Should().HaveCount(1); - string[] expectedAttributes = new string[] { "TestCategory : derived" }; + string[] expectedAttributes = ["TestCategory : derived"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -160,7 +154,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributesWithBaseInherita attributes.Should().NotBeNull(); attributes.Should().HaveCount(2); - string[] expectedAttributes = new string[] { "TestCategory : derived", "TestCategory : base", }; + string[] expectedAttributes = ["TestCategory : derived", "TestCategory : base"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -173,7 +167,7 @@ public void GetCustomAttributesShouldReturnAllAttributesIncludingUserDefinedAttr attributes.Should().NotBeNull(); attributes.Should().HaveCount(3); - string[] expectedAttributes = new string[] { "Duration : superfast", "TestCategory : base", "Owner : base" }; + string[] expectedAttributes = ["Duration : superfast", "TestCategory : base", "Owner : base"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -186,7 +180,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributesIncludingUserDef attributes.Should().NotBeNull(); attributes.Should().HaveCount(1); - string[] expectedAttributes = new string[] { "Duration : superfast" }; + string[] expectedAttributes = ["Duration : superfast"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -199,7 +193,7 @@ public void GetSpecificCustomAttributesShouldReturnArrayAttributesAsWell() attributes.Should().NotBeNull(); attributes.Should().HaveCount(1); - string[] expectedAttributes = new string[] { "CategoryAttribute : foo,foo2" }; + string[] expectedAttributes = ["CategoryAttribute : foo,foo2"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -212,7 +206,7 @@ public void GetSpecificCustomAttributesOnTypeShouldReturnAllAttributes() attributes.Should().NotBeNull(); attributes.Should().HaveCount(1); - string[] expectedAttributes = new string[] { "TestCategory : ba" }; + string[] expectedAttributes = ["TestCategory : ba"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -225,7 +219,7 @@ public void GetSpecificCustomAttributesOnTypeShouldReturnAllAttributesIgnoringBa attributes.Should().NotBeNull(); attributes.Should().HaveCount(1); - string[] expectedAttributes = new string[] { "TestCategory : a" }; + string[] expectedAttributes = ["TestCategory : a"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -238,7 +232,7 @@ public void GetSpecificCustomAttributesOnTypeShouldReturnAllAttributesWithBaseIn attributes.Should().NotBeNull(); attributes.Should().HaveCount(2); - string[] expectedAttributes = new string[] { "TestCategory : a", "TestCategory : ba" }; + string[] expectedAttributes = ["TestCategory : a", "TestCategory : ba"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -251,7 +245,7 @@ public void GetSpecificCustomAttributesOnAssemblyShouldReturnAllAttributes() attributes.Should().NotBeNull(); attributes.Should().HaveCount(2); - string[] expectedAttributes = new string[] { "TestCategory : a1", "TestCategory : a2" }; + string[] expectedAttributes = ["TestCategory : a1", "TestCategory : a2"]; GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } @@ -259,19 +253,7 @@ private Assembly ReflectionOnlyOnResolve(object sender, ResolveEventArgs args) { string assemblyNameToLoad = AppDomain.CurrentDomain.ApplyPolicy(args.Name); - // Put it in the resolved assembly cache so that if the Load call below - // triggers another assembly resolution, then we don't end up in stack overflow. - _resolvedAssemblies[assemblyNameToLoad] = null; - - var assembly = Assembly.ReflectionOnlyLoad(assemblyNameToLoad); - - if (assembly != null) - { - _resolvedAssemblies[assemblyNameToLoad] = assembly; - return assembly; - } - - return null; + return Assembly.ReflectionOnlyLoad(assemblyNameToLoad); } private static string[] GetAttributeValuePairs(IEnumerable attributes) @@ -279,25 +261,21 @@ private static string[] GetAttributeValuePairs(IEnumerable attributes) var attributeValuePairs = new List(); foreach (object attribute in attributes) { - if (attribute is OwnerAttribute) + if (attribute is OwnerAttribute ownerAttribute) { - var a = attribute as OwnerAttribute; - attributeValuePairs.Add("Owner : " + a.Owner); + attributeValuePairs.Add("Owner : " + ownerAttribute.Owner); } - else if (attribute is TestCategoryAttribute) + else if (attribute is TestCategoryAttribute categoryAttribute) { - var a = attribute as TestCategoryAttribute; - attributeValuePairs.Add("TestCategory : " + a.TestCategories.Aggregate((i, j) => i + "," + j)); + attributeValuePairs.Add("TestCategory : " + categoryAttribute.TestCategories.Aggregate((i, j) => i + "," + j)); } - else if (attribute is DurationAttribute) + else if (attribute is DurationAttribute durationAttribute) { - var a = attribute as DurationAttribute; - attributeValuePairs.Add("Duration : " + a.Duration); + attributeValuePairs.Add("Duration : " + durationAttribute.Duration); } - else if (attribute is CategoryArrayAttribute) + else if (attribute is CategoryArrayAttribute arrayAttribute) { - var a = attribute as CategoryArrayAttribute; - attributeValuePairs.Add("CategoryAttribute : " + a.Value.Aggregate((i, j) => i + "," + j)); + attributeValuePairs.Add("CategoryAttribute : " + arrayAttribute.Value.Aggregate((i, j) => i + "," + j)); } } diff --git a/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_OverriddenGetDisplayName.cs b/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_OverriddenGetDisplayName.cs index 89e75d0029..05f8667f1b 100644 --- a/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_OverriddenGetDisplayName.cs +++ b/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_OverriddenGetDisplayName.cs @@ -17,11 +17,6 @@ public class DataRowTests_OverriddenGetDisplayName private class DummyDataRowAttribute : DataRowAttribute { - public DummyDataRowAttribute() - : base() - { - } - public override string GetDisplayName(MethodInfo methodInfo, object[] data) => "Overridden DisplayName"; } } diff --git a/test/IntegrationTests/TestAssets/DiscoverInternalsProject/UnitTest1.cs b/test/IntegrationTests/TestAssets/DiscoverInternalsProject/UnitTest1.cs index 67702e2732..342e3d8e75 100644 --- a/test/IntegrationTests/TestAssets/DiscoverInternalsProject/UnitTest1.cs +++ b/test/IntegrationTests/TestAssets/DiscoverInternalsProject/UnitTest1.cs @@ -27,9 +27,7 @@ public void NestedInternalClass_TestMethod1() } } -internal class FancyString -{ -} +internal class FancyString; public abstract class CaseInsensitivityTests { @@ -52,9 +50,7 @@ internal class FancyStringsAreCaseInsensitive : CaseInsensitivityTests DynamicData => new[] - { - new object[] { new SerializableInternalType() }, - }; + public static IEnumerable DynamicData => + [ + [new SerializableInternalType()] + ]; } diff --git a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DataProvider.cs b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DataProvider.cs index 7272d2c8f6..98eda7ca9a 100644 --- a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DataProvider.cs +++ b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DataProvider.cs @@ -11,50 +11,50 @@ public class DataProvider { public static IEnumerable GetUserDataAndExceptedParsedUser() { - yield return new object[] - { + yield return + [ "John;Doe", new User() { FirstName = "John", LastName = "Doe", - }, - }; + } + ]; - yield return new object[] - { + yield return + [ "Jane;Doe", new User() { FirstName = "Jane", LastName = "Doe", - }, - }; + } + ]; } public static IEnumerable UserDataAndExceptedParsedUser { get { - yield return new object[] - { + yield return + [ "John;Doe", new User() { FirstName = "John", LastName = "Doe", - }, - }; + } + ]; - yield return new object[] - { + yield return + [ "Jane;Doe", new User() { FirstName = "Jane", LastName = "Doe", - }, - }; + } + ]; } } diff --git a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DynamicDataTests.cs b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DynamicDataTests.cs index b0005755a6..0053533054 100644 --- a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DynamicDataTests.cs +++ b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DynamicDataTests.cs @@ -98,50 +98,50 @@ private static void ParseAndAssert(string userData, User expectedUser) public static IEnumerable GetParseUserData() { - yield return new object[] - { + yield return + [ "John;Doe", new User() { FirstName = "John", LastName = "Doe", - }, - }; + } + ]; - yield return new object[] - { + yield return + [ "Jane;Doe", new User() { FirstName = "Jane", LastName = "Doe", - }, - }; + } + ]; } public static IEnumerable ParseUserData { get { - yield return new object[] - { + yield return + [ "John;Doe", new User() { FirstName = "John", LastName = "Doe", - }, - }; + } + ]; - yield return new object[] - { + yield return + [ "Jane;Doe", new User() { FirstName = "Jane", LastName = "Doe", - }, - }; + } + ]; } } diff --git a/test/IntegrationTests/TestAssets/FixturesTestProject/FixturesTestProject.csproj b/test/IntegrationTests/TestAssets/FixturesTestProject/FixturesTestProject.csproj new file mode 100644 index 0000000000..c272e52833 --- /dev/null +++ b/test/IntegrationTests/TestAssets/FixturesTestProject/FixturesTestProject.csproj @@ -0,0 +1,12 @@ + + + + net462 + false + + + + + + + diff --git a/test/IntegrationTests/TestAssets/FixturesTestProject/UnitTest1.cs b/test/IntegrationTests/TestAssets/FixturesTestProject/UnitTest1.cs new file mode 100644 index 0000000000..dac12917b2 --- /dev/null +++ b/test/IntegrationTests/TestAssets/FixturesTestProject/UnitTest1.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace FixturesTestProject1; + +[TestClass] +public class UnitTest1 +{ + [AssemblyInitialize] + public static void AssemblyInitialize(TestContext context) + { + bool? condition = GetCondition("AssemblyInitialize"); + Assert.IsNotNull(condition); + Assert.IsTrue(condition.Value); + } + + [AssemblyCleanup] + public static void AssemblyCleanup() + { + bool? condition = GetCondition("AssemblyCleanup"); + Assert.IsNotNull(condition); + Assert.IsTrue(condition.Value); + } + + [ClassInitialize] + public static void ClassInitialize(TestContext testContext) + { + bool? condition = GetCondition("ClassInitialize"); + Assert.IsNotNull(condition); + Assert.IsTrue(condition.Value); + } + + [ClassCleanup] + public static void ClassCleanup() + { + bool? condition = GetCondition("ClassCleanup"); + Assert.IsNotNull(condition); + Assert.IsTrue(condition.Value); + } + + [TestMethod] + public void PassingTest() => Assert.IsTrue(true); + + [TestMethod] + public void Test() + { + bool? condition = GetCondition("Test"); + Assert.IsNotNull(condition); + Assert.IsTrue(condition.Value); + } + + private static bool? GetCondition(string environmentVariable) + => bool.TryParse(Environment.GetEnvironmentVariable(environmentVariable), out bool result) + ? result + : null; +} diff --git a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/DynamicDataExMoreTests.cs b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/DynamicDataExMoreTests.cs index 6fef628b96..db7c62cd6a 100644 --- a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/DynamicDataExMoreTests.cs +++ b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/DynamicDataExMoreTests.cs @@ -37,4 +37,32 @@ public void DynamicDataTestMethod6(string a, int b, bool c) Assert.AreEqual(0, b % 2); Assert.IsTrue(c); } + + [TestMethod] + [DynamicData("EmptyTestDataProperty", typeof(DynamicDataExTests))] + public void DynamicEmptyDataTestMethod4(string a, int b, bool c) + { + Assert.AreEqual("string", a); + Assert.AreEqual(0, b % 2); + Assert.IsTrue(c); + } + + [TestMethod] + [DynamicData("EmptyTestDataMethod", typeof(DynamicDataExTests), DynamicDataSourceType.Method)] + public void DynamicEmptyDataTestMethod5(string a, int b, bool c) + { + Assert.AreEqual("string", a); + Assert.AreEqual(0, b % 2); + Assert.IsTrue(c); + } + + [TestMethod] + [DynamicData("EmptyTestDataProperty", typeof(DynamicDataExTests))] + [DynamicData("EmptyTestDataMethod", typeof(DynamicDataExTests), DynamicDataSourceType.Method)] + public void DynamicEmptyDataTestMethod6(string a, int b, bool c) + { + Assert.AreEqual("string", a); + Assert.AreEqual(0, b % 2); + Assert.IsTrue(c); + } } diff --git a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/DynamicDataExTests.cs b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/DynamicDataExTests.cs index 29df78043d..a11da35b37 100644 --- a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/DynamicDataExTests.cs +++ b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/DynamicDataExTests.cs @@ -9,11 +9,15 @@ namespace FxExtensibilityTestProject; public class DynamicDataExTests { #pragma warning disable IDE0051 // Remove unused private members - private static IEnumerable ReusableTestDataProperty => new[] { new object[] { "string", 2, true } }; + private static IEnumerable ReusableTestDataProperty => [["string", 2, true]]; + + private static IEnumerable EmptyTestDataProperty => []; #pragma warning disable CA1859 // Use concrete types when possible for improved performance #pragma warning disable IDE0051 // Remove unused private members - private static IEnumerable ReusableTestDataMethod() => new[] { new object[] { "string", 4, true } }; + private static IEnumerable ReusableTestDataMethod() => [["string", 4, true]]; + + private static IEnumerable EmptyTestDataMethod() => []; // Property ReusableTestDataProperty can be used as data source for test data with data driven test case. [TestMethod] @@ -44,4 +48,32 @@ public void DynamicDataTestMethod3(string a, int b, bool c) Assert.AreEqual(0, b % 2); Assert.IsTrue(c); } + + [TestMethod] + [DynamicData("EmptyTestDataProperty")] + public void DynamicEmptyDataTestMethod1(string a, int b, bool c) + { + Assert.AreEqual("string", a); + Assert.AreEqual(0, b % 2); + Assert.IsTrue(c); + } + + [TestMethod] + [DynamicData("EmptyTestDataMethod", DynamicDataSourceType.Method)] + public void DynamicEmptyDataTestMethod2(string a, int b, bool c) + { + Assert.AreEqual("string", a); + Assert.AreEqual(0, b % 2); + Assert.IsTrue(c); + } + + [TestMethod] + [DynamicData("EmptyTestDataProperty")] + [DynamicData("EmptyTestDataMethod", DynamicDataSourceType.Method)] + public void DynamicEmptyDataTestMethod3(string a, int b, bool c) + { + Assert.AreEqual("string", a); + Assert.AreEqual(0, b % 2); + Assert.IsTrue(c); + } } diff --git a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/TestDataSourceExTests.cs b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/TestDataSourceExTests.cs index d8fd9d1e4f..b9a171c4b1 100644 --- a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/TestDataSourceExTests.cs +++ b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/TestDataSourceExTests.cs @@ -13,6 +13,7 @@ public class TestDataSourceExTests { [TestMethod] [CustomTestDataSource] + [CustomEmptyTestDataSource] public void CustomTestDataSourceTestMethod1(int a, int b, int c) { Assert.AreEqual(1, a % 3); @@ -24,7 +25,15 @@ public void CustomTestDataSourceTestMethod1(int a, int b, int c) [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class CustomTestDataSourceAttribute : Attribute, ITestDataSource { - public IEnumerable GetData(MethodInfo methodInfo) => new[] { new object[] { 1, 2, 3 }, [4, 5, 6] }; + public IEnumerable GetData(MethodInfo methodInfo) => [[1, 2, 3], [4, 5, 6]]; + + public string GetDisplayName(MethodInfo methodInfo, object[] data) => data != null ? string.Format(CultureInfo.CurrentCulture, "{0} ({1})", methodInfo.Name, string.Join(",", data)) : null; +} + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] +public class CustomEmptyTestDataSourceAttribute : Attribute, ITestDataSource +{ + public IEnumerable GetData(MethodInfo methodInfo) => []; public string GetDisplayName(MethodInfo methodInfo, object[] data) => data != null ? string.Format(CultureInfo.CurrentCulture, "{0} ({1})", methodInfo.Name, string.Join(",", data)) : null; } diff --git a/test/IntegrationTests/TestAssets/SampleFrameworkExtensions/ExtensionAttributes.cs b/test/IntegrationTests/TestAssets/SampleFrameworkExtensions/ExtensionAttributes.cs index bed3abdebe..77910c4102 100644 --- a/test/IntegrationTests/TestAssets/SampleFrameworkExtensions/ExtensionAttributes.cs +++ b/test/IntegrationTests/TestAssets/SampleFrameworkExtensions/ExtensionAttributes.cs @@ -16,7 +16,7 @@ public DurationAttribute(string duration) public string Duration { get; private set; } } -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method)] public sealed class CategoryArrayAttribute : Attribute { public CategoryArrayAttribute(params string[] value) diff --git a/test/IntegrationTests/TestAssets/TestIdProject.DefaultStrategy/TestIdCases.cs b/test/IntegrationTests/TestAssets/TestIdProject.DefaultStrategy/TestIdCases.cs index 0dfaf7c317..7ef1d44620 100644 --- a/test/IntegrationTests/TestAssets/TestIdProject.DefaultStrategy/TestIdCases.cs +++ b/test/IntegrationTests/TestAssets/TestIdProject.DefaultStrategy/TestIdCases.cs @@ -45,9 +45,9 @@ public static IEnumerable ArraysData { get { - yield return new object[] { 0, Array.Empty() }; - yield return new object[] { 0, new int[] { 0 } }; - yield return new object[] { 0, new int[] { 0, 0, 0 } }; + yield return [0, Array.Empty()]; + yield return [0, new int[] { 0 }]; + yield return [0, new int[] { 0, 0, 0 }]; } } @@ -61,8 +61,8 @@ public static IEnumerable TuplesData { get { - yield return new object[] { (1, "text", true) }; - yield return new object[] { (1, "text", false) }; + yield return [(1, "text", true)]; + yield return [(1, "text", false)]; } } @@ -76,10 +76,10 @@ public static IEnumerable GenericCollectionsData { get { - yield return new object[] { new List { 1, 2, 3 }, new List { "a", "b", "c" }, new List { true, false, true } }; - yield return new object[] { new List { 1, 2 }, new List { "a", "b", "c" }, new List { true, false, true } }; - yield return new object[] { new List { 1, 2, 3 }, new List { "a", "b" }, new List { true, false, true } }; - yield return new object[] { new List { 1, 2, 3 }, new List { "a", "b", "c" }, new List { true, false } }; + yield return [new List { 1, 2, 3 }, new List { "a", "b", "c" }, new List { true, false, true }]; + yield return [new List { 1, 2 }, new List { "a", "b", "c" }, new List { true, false, true }]; + yield return [new List { 1, 2, 3 }, new List { "a", "b" }, new List { true, false, true }]; + yield return [new List { 1, 2, 3 }, new List { "a", "b", "c" }, new List { true, false }]; } } diff --git a/test/Performance/MSTest.Performance.Runner/IPayload.cs b/test/Performance/MSTest.Performance.Runner/IPayload.cs index 73f5169760..a3891acbde 100644 --- a/test/Performance/MSTest.Performance.Runner/IPayload.cs +++ b/test/Performance/MSTest.Performance.Runner/IPayload.cs @@ -3,6 +3,4 @@ namespace MSTest.Performance.Runner; -internal interface IPayload -{ -} +internal interface IPayload; diff --git a/test/Performance/MSTest.Performance.Runner/PipelinesRunner.cs b/test/Performance/MSTest.Performance.Runner/PipelinesRunner.cs index e6dedeb9e8..de01ee3219 100644 --- a/test/Performance/MSTest.Performance.Runner/PipelinesRunner.cs +++ b/test/Performance/MSTest.Performance.Runner/PipelinesRunner.cs @@ -28,7 +28,7 @@ public int Run(string pipelineNameFilter, IDictionary? parameter continue; } - if (!pipeline.OSPlatform.Any(x => RuntimeInformation.IsOSPlatform(x))) + if (!pipeline.OSPlatform.Any(RuntimeInformation.IsOSPlatform)) { WriteConsole($"Skip '{pipeline.PipelineName}', OS expected: '{pipeline.OSPlatform}', current OS: '{RuntimeInformation.OSDescription}'", ConsoleColor.Yellow); continue; @@ -48,10 +48,7 @@ public int Run(string pipelineNameFilter, IDictionary? parameter pipelinePropertyBag.Add(item.Key, item.Value); } - if (pipeline.UpdatePropertyBag is not null) - { - pipeline.UpdatePropertyBag(pipelinePropertyBag); - } + pipeline.UpdatePropertyBag?.Invoke(pipelinePropertyBag); pipeline.Func(pipelinePropertyBag); } @@ -73,5 +70,5 @@ private static void WriteConsole(string message, ConsoleColor consoleColor) } } - private record class PipelineInfo(string GroupName, string PipelineName, OSPlatform[] OSPlatform, Action> Func, Action>? UpdatePropertyBag = null, string[]? Traits = null); + private record PipelineInfo(string GroupName, string PipelineName, OSPlatform[] OSPlatform, Action> Func, Action>? UpdatePropertyBag = null, string[]? Traits = null); } diff --git a/test/Performance/MSTest.Performance.Runner/Program.cs b/test/Performance/MSTest.Performance.Runner/Program.cs index 7274888fa3..a5e94f2991 100644 --- a/test/Performance/MSTest.Performance.Runner/Program.cs +++ b/test/Performance/MSTest.Performance.Runner/Program.cs @@ -14,14 +14,13 @@ namespace MSTest.Performance.Runner; internal class EntryPoint { - public static int Main(string[] args) + public static Task Main(string[] args) { // Opt out telemetry for clean stacks, AppInsight is allocating strings and polluting the results. Environment.SetEnvironmentVariable("DOTNET_CLI_TELEMETRY_OPTOUT", "1"); Console.WriteLine("Microsoft (R) MSTest Performance Profiler Command Line Tool"); - int exitCode = 0; var rootCommand = new RootCommand("MSTest Performance Profiler Command Line Tool"); var pipelineNameFilter = new Option(name: "--pipelineNameFilter", description: "Globbing filter for the pipeline name to execute.", getDefaultValue: () => string.Empty); var executeTests = new Command("execute", "Execute the performance scenarios.") @@ -33,15 +32,14 @@ public static int Main(string[] args) rootCommand.AddCommand(executeTests); - exitCode = rootCommand.InvokeAsync(args).Result; - return exitCode; + return rootCommand.InvokeAsync(args); } private static int Pipelines(string pipelineNameFilter) { var pipelineRunner = new PipelinesRunner(); - pipelineRunner.AddPipeline("Default", "Scenario1_PerfView", new[] { OSPlatform.Windows }, parametersBag => + pipelineRunner.AddPipeline("Default", "Scenario1_PerfView", [OSPlatform.Windows], parametersBag => Pipeline .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net8.0", executionScope: ExecutionScope.MethodLevel), parametersBag) .NextStep(() => new DotnetMuxer(BuildConfiguration.Debug)) @@ -49,7 +47,7 @@ private static int Pipelines(string pipelineNameFilter) .NextStep(() => new MoveFiles("*.zip", Path.Combine(Directory.GetCurrentDirectory(), "Results"))) .NextStep(() => new CleanupDisposable())); - pipelineRunner.AddPipeline("Default", "Scenario1_DotnetTrace", new[] { OSPlatform.Windows }, parametersBag => + pipelineRunner.AddPipeline("Default", "Scenario1_DotnetTrace", [OSPlatform.Windows], parametersBag => Pipeline .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net8.0", executionScope: ExecutionScope.MethodLevel), parametersBag) .NextStep(() => new DotnetMuxer(BuildConfiguration.Debug)) @@ -58,14 +56,14 @@ private static int Pipelines(string pipelineNameFilter) .NextStep(() => new CleanupDisposable())); // C:\Program Files\Microsoft Visual Studio\2022\Preview\Team Tools\DiagnosticsHub\Collector\AgentConfigs - pipelineRunner.AddPipeline("Default", "Scenario1_DotNetObjectAllocBase", new[] { OSPlatform.Windows }, parametersBag => + pipelineRunner.AddPipeline("Default", "Scenario1_DotNetObjectAllocBase", [OSPlatform.Windows], parametersBag => Pipeline .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net8.0", executionScope: ExecutionScope.MethodLevel), parametersBag) .NextStep(() => new DotnetMuxer(BuildConfiguration.Debug)) .NextStep(() => new VSDiagnostics("DotNetObjectAllocLow.json", "Scenario1_DotNetObjectAllocBase.zip")) .NextStep(() => new MoveFiles("*.zip", Path.Combine(Directory.GetCurrentDirectory(), "Results"))) .NextStep(() => new CleanupDisposable())); - pipelineRunner.AddPipeline("Default", "Scenario1_CpuUsageLow", new[] { OSPlatform.Windows }, parametersBag => + pipelineRunner.AddPipeline("Default", "Scenario1_CpuUsageLow", [OSPlatform.Windows], parametersBag => Pipeline .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net8.0", executionScope: ExecutionScope.MethodLevel), parametersBag) .NextStep(() => new DotnetMuxer(BuildConfiguration.Debug)) @@ -73,7 +71,7 @@ private static int Pipelines(string pipelineNameFilter) .NextStep(() => new MoveFiles("*.zip", Path.Combine(Directory.GetCurrentDirectory(), "Results"))) .NextStep(() => new CleanupDisposable())); - pipelineRunner.AddPipeline("Default", "Scenario1_ConcurrencyVisualizer", new[] { OSPlatform.Windows }, parametersBag => + pipelineRunner.AddPipeline("Default", "Scenario1_ConcurrencyVisualizer", [OSPlatform.Windows], parametersBag => Pipeline .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net8.0", executionScope: ExecutionScope.MethodLevel), parametersBag) .NextStep(() => new DotnetMuxer(BuildConfiguration.Debug)) @@ -81,7 +79,7 @@ private static int Pipelines(string pipelineNameFilter) .NextStep(() => new MoveFiles("*.zip", Path.Combine(Directory.GetCurrentDirectory(), "Results"))) .NextStep(() => new CleanupDisposable())); - pipelineRunner.AddPipeline("Default", "Scenario1_PlainProcess", new[] { OSPlatform.Windows }, parametersBag => + pipelineRunner.AddPipeline("Default", "Scenario1_PlainProcess", [OSPlatform.Windows], parametersBag => Pipeline .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net8.0", executionScope: ExecutionScope.MethodLevel), parametersBag) .NextStep(() => new DotnetMuxer(BuildConfiguration.Debug)) diff --git a/test/Performance/MSTest.Performance.Runner/Scenarios/Scenario1.cs b/test/Performance/MSTest.Performance.Runner/Scenarios/Scenario1.cs index 95ccd4a818..33768cd2dd 100644 --- a/test/Performance/MSTest.Performance.Runner/Scenarios/Scenario1.cs +++ b/test/Performance/MSTest.Performance.Runner/Scenarios/Scenario1.cs @@ -47,32 +47,44 @@ public async Task ExecuteAsync(NoInputOutput payload, IContext co StringBuilder stringBuilder = new(); for (int i = 0; i < _numberOfClass; i++) { - stringBuilder.AppendLine(CultureInfo.InvariantCulture, $@" -[TestClass] -public class UnitTest{i} -{{"); + stringBuilder.AppendLine( + CultureInfo.InvariantCulture, + $$""" + + [TestClass] + public class UnitTest{{i}} + { + """); for (int k = 1; k < _methodsPerClass + 1; k++) { if (k % 2 == 0) { - stringBuilder.AppendLine(CultureInfo.InvariantCulture, $@" - [TestMethod] - [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - public System.Threading.Tasks.Task TestMethod{k}() - {{ - return System.Threading.Tasks.Task.CompletedTask; - }} -"); + stringBuilder.AppendLine( + CultureInfo.InvariantCulture, + $$""" + + [TestMethod] + [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + public System.Threading.Tasks.Task TestMethod{{k}}() + { + return System.Threading.Tasks.Task.CompletedTask; + } + + """); } else { - stringBuilder.AppendLine(CultureInfo.InvariantCulture, $@" - [TestMethod] - [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - public void TestMethod{k}() - {{ - }} -"); + stringBuilder.AppendLine( + CultureInfo.InvariantCulture, + $$""" + + [TestMethod] + [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + public void TestMethod{{k}}() + { + } + + """); } } @@ -94,7 +106,7 @@ public class UnitTest{i} addPublicFeeds: true); context.AddDisposable(generator); - return new SingleProject(new string[] { "net8.0" }, generator, nameof(Scenario1)); + return new SingleProject(["net8.0"], generator, nameof(Scenario1)); } private static string ExtractVersionFromPackage(string rootFolder, string packagePrefixName) diff --git a/test/Performance/MSTest.Performance.Runner/Steps/ConcurrencyVisualizer.cs b/test/Performance/MSTest.Performance.Runner/Steps/ConcurrencyVisualizer.cs index a2d2a547c0..dfce5ac760 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/ConcurrencyVisualizer.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/ConcurrencyVisualizer.cs @@ -29,7 +29,7 @@ public async Task ExecuteAsync(BuildArtifact payload, IContext context) if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Console.WriteLine("Skip run, not supported in Windows"); - return new Files(Array.Empty()); + return new Files([]); } string vsProgramFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Microsoft Visual Studio"); @@ -128,6 +128,6 @@ public async Task ExecuteAsync(BuildArtifact payload, IContext context) Console.WriteLine($"Compressing to '{sample}'"); ZipFile.CreateFromDirectory(payload.TestAsset.TargetAssetPath, sample, _compressionLevel, includeBaseDirectory: true); - return new Files(new[] { sample }); + return new Files([sample]); } } diff --git a/test/Performance/MSTest.Performance.Runner/Steps/DotnetTrace.cs b/test/Performance/MSTest.Performance.Runner/Steps/DotnetTrace.cs index 26176cc753..4214fc206c 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/DotnetTrace.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/DotnetTrace.cs @@ -55,6 +55,6 @@ public async Task ExecuteAsync(BuildArtifact payload, IContext context) Console.WriteLine($"Compressing to '{sample}'"); ZipFile.CreateFromDirectory(payload.TestAsset.TargetAssetPath, sample, _compressionLevel, includeBaseDirectory: true); - return new Files(new[] { sample }); + return new Files([sample]); } } diff --git a/test/Performance/MSTest.Performance.Runner/Steps/PerfviewRunner.cs b/test/Performance/MSTest.Performance.Runner/Steps/PerfviewRunner.cs index 91bd3dd981..fe71f6f2b3 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/PerfviewRunner.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/PerfviewRunner.cs @@ -32,10 +32,10 @@ public async Task ExecuteAsync(BuildArtifact payload, IContext context) if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Console.WriteLine("Skip run, not supported in Windows"); - return new Files(Array.Empty()); + return new Files([]); } - string perfViewExecutable = await PerfviewExecutable(); + await PerfviewExecutable(); StringBuilder commandLine = new(); commandLine.Append(CultureInfo.InvariantCulture, $" \"/DataFile:{Path.Combine(Path.GetDirectoryName(payload.TestHost.FullName)!, "DataFile.etl")}\" /AcceptEULA /NoGui {_argument} "); commandLine.Append(CultureInfo.InvariantCulture, $"run \"{payload.TestHost.FullName}\" "); @@ -59,12 +59,12 @@ public async Task ExecuteAsync(BuildArtifact payload, IContext context) process.OutputDataReceived += (sender, args) => { Console.WriteLine(args.Data); - if (args?.Data?.Contains("SUCCESS: PerfView") == true) + if (args.Data?.Contains("SUCCESS: PerfView") == true) { succeded = true; } - if (args?.Data == "Press enter to close window.") + if (args.Data == "Press enter to close window.") { killTheProcess.Set(); } @@ -98,13 +98,13 @@ public async Task ExecuteAsync(BuildArtifact payload, IContext context) Directory.CreateDirectory(dataFileDirectory); foreach (string item in Directory.GetFiles(reportDirectory, "DataFile.*")) { - File.Move(item, Path.Combine(dataFileDirectory, Path.GetFileName(item)!)); + File.Move(item, Path.Combine(dataFileDirectory, Path.GetFileName(item))); } ZipFile.CreateFromDirectory(dataFileDirectory, sample, _compressionLevel, includeBaseDirectory: true); } - return new Files(new[] { sample }); + return new Files([sample]); } private async Task PerfviewExecutable() @@ -116,13 +116,11 @@ private async Task PerfviewExecutable() return localPath; } - using (HttpClient client = new()) - { - using HttpResponseMessage response = await client.GetAsync(PrefViewDownload); - using Stream streamToReadFrom = await response.Content.ReadAsStreamAsync(); - using Stream streamToWriteTo = File.Open(localPath, FileMode.Create); - await streamToReadFrom.CopyToAsync(streamToWriteTo); - } + using HttpClient client = new(); + using HttpResponseMessage response = await client.GetAsync(PrefViewDownload); + using Stream streamToReadFrom = await response.Content.ReadAsStreamAsync(); + using Stream streamToWriteTo = File.Open(localPath, FileMode.Create); + await streamToReadFrom.CopyToAsync(streamToWriteTo); return localPath; } diff --git a/test/Performance/MSTest.Performance.Runner/Steps/PlainProcess.cs b/test/Performance/MSTest.Performance.Runner/Steps/PlainProcess.cs index 5664e7c6c7..73538f326e 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/PlainProcess.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/PlainProcess.cs @@ -65,6 +65,6 @@ await File.AppendAllTextAsync(Path.Combine(Path.GetDirectoryName(payload.TestHos ZipFile.CreateFromDirectory(payload.TestAsset.TargetAssetPath, sample, _compressionLevel, includeBaseDirectory: true); - return new Files(new[] { sample }); + return new Files([sample]); } } diff --git a/test/Performance/MSTest.Performance.Runner/Steps/VSDiagnostics.cs b/test/Performance/MSTest.Performance.Runner/Steps/VSDiagnostics.cs index cbdb1191f5..e286461c47 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/VSDiagnostics.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/VSDiagnostics.cs @@ -27,7 +27,7 @@ public async Task ExecuteAsync(BuildArtifact payload, IContext context) if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Console.WriteLine("Skip run, not supported in Windows"); - return new Files(Array.Empty()); + return new Files([]); } string vsProgramFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Microsoft Visual Studio"); @@ -90,6 +90,6 @@ public async Task ExecuteAsync(BuildArtifact payload, IContext context) Console.WriteLine($"Compressing to '{sample}'"); ZipFile.CreateFromDirectory(payload.TestAsset.TargetAssetPath, sample, _compressionLevel, includeBaseDirectory: true); - return new Files(new[] { sample }); + return new Files([sample]); } } diff --git a/test/Performance/MSTest.Performance.Runner/Steps/WindowsProcessWatcher.cs b/test/Performance/MSTest.Performance.Runner/Steps/WindowsProcessWatcher.cs index 889658a8ad..10b04a95fe 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/WindowsProcessWatcher.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/WindowsProcessWatcher.cs @@ -17,7 +17,7 @@ public WindowsProcessWatcher(string processName) FROM __InstanceOperationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' and TargetInstance.Name = '{processName}' """; - EventArrived += new EventArrivedEventHandler(Watcher_EventArrived); + EventArrived += Watcher_EventArrived; } public event EventHandler? ProcessCreated; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyCleanupShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyCleanupShouldBeValidAnalyzerTests.cs index 4dccc38384..ad6c0b52bb 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyCleanupShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyCleanupShouldBeValidAnalyzerTests.cs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.AssemblyCleanupShouldBeValidAnalyzer, - Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + MSTest.Analyzers.AssemblyCleanupShouldBeValidFixer>; namespace MSTest.Analyzers.Test; @@ -46,11 +43,10 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyCleanupShouldBeValidAnalyzer.NotAGenericClassRule) - .WithLocation(0) - .WithArguments("AssemblyCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyCleanup"), + code); } public async Task WhenAssemblyCleanupIsNotOrdinary_Diagnostic() @@ -68,11 +64,10 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyCleanupShouldBeValidAnalyzer.OrdinaryRule) - .WithLocation(0) - .WithArguments("Finalize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("Finalize"), + code); } public async Task WhenAssemblyCleanupIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() @@ -112,11 +107,25 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [assembly: DiscoverInternals] + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public static void AssemblyCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyCleanupShouldBeValidAnalyzer.PublicRule) - .WithLocation(0) - .WithArguments("AssemblyCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyCleanup"), + fixedCode); } [Arguments("protected")] @@ -138,11 +147,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public static void AssemblyCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyCleanupShouldBeValidAnalyzer.PublicRule) - .WithLocation(0) - .WithArguments("AssemblyCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyCleanup"), + fixedCode); } public async Task WhenAssemblyCleanupIsGeneric_Diagnostic() @@ -160,11 +181,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public static void AssemblyCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyCleanupShouldBeValidAnalyzer.NotGenericRule) - .WithLocation(0) - .WithArguments("AssemblyCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyCleanup"), + fixedCode); } public async Task WhenAssemblyCleanupIsNotStatic_Diagnostic() @@ -182,11 +215,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public static void AssemblyCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyCleanupShouldBeValidAnalyzer.StaticRule) - .WithLocation(0) - .WithArguments("AssemblyCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyCleanup"), + fixedCode); } public async Task WhenAssemblyCleanupHasParameters_Diagnostic() @@ -204,11 +249,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public static void AssemblyCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyCleanupShouldBeValidAnalyzer.NoParametersRule) - .WithLocation(0) - .WithArguments("AssemblyCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyCleanup"), + fixedCode); } public async Task WhenAssemblyCleanupReturnTypeIsNotValid_Diagnostic() @@ -223,43 +280,99 @@ public class MyTestClass [AssemblyCleanup] public static int {|#0:AssemblyCleanup0|}() { + int x = 1 + 2; return 0; } [AssemblyCleanup] public static string {|#1:AssemblyCleanup1|}() { + int x = 1 + 2; return "0"; } [AssemblyCleanup] - public static Task {|#2:AssemblyCleanup2|}() + public static async Task {|#2:AssemblyCleanup2|}() + { + await Task.Delay(0); + return 0; + } + + [AssemblyCleanup] + public static Task {|#3:AssemblyCleanup3|}() { return Task.FromResult(0); } [AssemblyCleanup] - public static ValueTask {|#3:AssemblyCleanup3|}() + public static async ValueTask {|#4:AssemblyCleanup4|}() + { + await Task.Delay(0); + return 0; + } + + [AssemblyCleanup] + public static ValueTask {|#5:AssemblyCleanup5|}() { return ValueTask.FromResult(0); } } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public static void AssemblyCleanup0() + { + int x = 1 + 2; + } + + [AssemblyCleanup] + public static void AssemblyCleanup1() + { + int x = 1 + 2; + } + + [AssemblyCleanup] + public static async Task AssemblyCleanup2() + { + await Task.Delay(0); + } + + [AssemblyCleanup] + public static Task {|CS0161:AssemblyCleanup3|}() + { + } + + [AssemblyCleanup] + public static async ValueTask AssemblyCleanup4() + { + await Task.Delay(0); + } + + [AssemblyCleanup] + public static ValueTask {|CS0161:AssemblyCleanup5|}() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyCleanupShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(0) - .WithArguments("AssemblyCleanup0"), - VerifyCS.Diagnostic(AssemblyCleanupShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(1) - .WithArguments("AssemblyCleanup1"), - VerifyCS.Diagnostic(AssemblyCleanupShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(2) - .WithArguments("AssemblyCleanup2"), - VerifyCS.Diagnostic(AssemblyCleanupShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(3) - .WithArguments("AssemblyCleanup3")); + [ + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyCleanup0"), + VerifyCS.Diagnostic().WithLocation(1).WithArguments("AssemblyCleanup1"), + VerifyCS.Diagnostic().WithLocation(2).WithArguments("AssemblyCleanup2"), + VerifyCS.Diagnostic().WithLocation(3).WithArguments("AssemblyCleanup3"), + VerifyCS.Diagnostic().WithLocation(4).WithArguments("AssemblyCleanup4"), + VerifyCS.Diagnostic().WithLocation(5).WithArguments("AssemblyCleanup5") + ], + fixedCode); } public async Task WhenAssemblyCleanupReturnTypeIsValid_NoDiagnostic() @@ -310,10 +423,96 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public static async Task AssemblyCleanup() + { + await Task.Delay(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyCleanupShouldBeValidAnalyzer.NotAsyncVoidRule) - .WithLocation(0) - .WithArguments("AssemblyCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyCleanup"), + fixedCode); + } + + public async Task WhenMultipleViolations_TheyAllGetFixed() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public async void {|#0:AssemblyCleanup|}(int i) + { + await Task.Delay(0); + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public static async Task AssemblyCleanup() + { + await Task.Delay(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyCleanup"), + fixedCode); + } + + public async Task WhenAssemblyCleanupIsNotOnClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public struct MyTestClass + { + [AssemblyCleanup] + public static void [|AssemblyCleanup|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenAssemblyCleanupIsOnClassNotMarkedWithTestClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class MyTestClass + { + [AssemblyCleanup] + public static void [|AssemblyCleanup|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyInitializeShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyInitializeShouldBeValidAnalyzerTests.cs index cca669e7eb..b6d2a58b3f 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyInitializeShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyInitializeShouldBeValidAnalyzerTests.cs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.AssemblyInitializeShouldBeValidAnalyzer, - Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + MSTest.Analyzers.AssemblyInitializeShouldBeValidFixer>; namespace MSTest.Analyzers.Test; @@ -22,7 +19,7 @@ public async Task WhenAssemblyInitializeIsPublic_NoDiagnostic() public class MyTestClass { [AssemblyInitialize] - public static void AssemblyInitialize(TestContext context) + public static void AssemblyInitialize(TestContext testContext) { } } @@ -35,14 +32,14 @@ public async Task WhenAssemblyInitializeIsPublic_InsideInternalClassWithDiscover { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; - + [assembly: DiscoverInternals] [TestClass] internal class MyTestClass { [AssemblyInitialize] - public static void AssemblyInitialize(TestContext context) + public static void AssemblyInitialize(TestContext testContext) { } } @@ -60,17 +57,16 @@ public async Task WhenAssemblyInitializeIsInsideAGenericClass_Diagnostic() public class MyTestClass { [AssemblyInitialize] - public static void {|#0:AssemblyInitialize|}(TestContext context) + public static void {|#0:AssemblyInitialize|}(TestContext testContext) { } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyInitializeShouldBeValidAnalyzer.NotAGenericClassRule) - .WithLocation(0) - .WithArguments("AssemblyInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyInitialize"), + code); } public async Task WhenAssemblyInitializeIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() @@ -84,17 +80,31 @@ public async Task WhenAssemblyInitializeIsInternal_InsidePublicClassWithDiscover public class MyTestClass { [AssemblyInitialize] - internal static void {|#0:AssemblyInitialize|}(TestContext context) + internal static void {|#0:AssemblyInitialize|}(TestContext testContext) { } } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [assembly: DiscoverInternals] + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static void AssemblyInitialize(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyInitializeShouldBeValidAnalyzer.PublicRule) - .WithLocation(0) - .WithArguments("AssemblyInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyInitialize"), + fixedCode); } [Arguments("protected")] @@ -110,17 +120,29 @@ public async Task WhenAssemblyInitializeIsNotPublic_Diagnostic(string accessibil public class MyTestClass { [AssemblyInitialize] - {{accessibility}} static void {|#0:AssemblyInitialize|}(TestContext context) + {{accessibility}} static void {|#0:AssemblyInitialize|}(TestContext testContext) { } } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static void AssemblyInitialize(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyInitializeShouldBeValidAnalyzer.PublicRule) - .WithLocation(0) - .WithArguments("AssemblyInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyInitialize"), + fixedCode); } public async Task WhenAssemblyInitializeIsNotOrdinary_Diagnostic() @@ -138,11 +160,10 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyInitializeShouldBeValidAnalyzer.OrdinaryRule) - .WithLocation(0) - .WithArguments("Finalize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("Finalize"), + code); } public async Task WhenAssemblyInitializeIsGeneric_Diagnostic() @@ -154,17 +175,29 @@ public async Task WhenAssemblyInitializeIsGeneric_Diagnostic() public class MyTestClass { [AssemblyInitialize] - public static void {|#0:AssemblyInitialize|}(TestContext context) + public static void {|#0:AssemblyInitialize|}(TestContext testContext) + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static void AssemblyInitialize(TestContext testContext) { } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyInitializeShouldBeValidAnalyzer.NotGenericRule) - .WithLocation(0) - .WithArguments("AssemblyInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyInitialize"), + fixedCode); } public async Task WhenAssemblyInitializeIsNotStatic_Diagnostic() @@ -176,17 +209,29 @@ public async Task WhenAssemblyInitializeIsNotStatic_Diagnostic() public class MyTestClass { [AssemblyInitialize] - public void {|#0:AssemblyInitialize|}(TestContext context) + public void {|#0:AssemblyInitialize|}(TestContext testContext) { } } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static void AssemblyInitialize(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyInitializeShouldBeValidAnalyzer.StaticRule) - .WithLocation(0) - .WithArguments("AssemblyInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyInitialize"), + fixedCode); } public async Task WhenAssemblyInitializeDoesNotHaveParameters_Diagnostic() @@ -204,11 +249,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static void AssemblyInitialize(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyInitializeShouldBeValidAnalyzer.SingleContextParameterRule) - .WithLocation(0) - .WithArguments("AssemblyInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyInitialize"), + fixedCode); } public async Task WhenAssemblyInitializeReturnTypeIsNotValid_Diagnostic() @@ -221,45 +278,97 @@ public async Task WhenAssemblyInitializeReturnTypeIsNotValid_Diagnostic() public class MyTestClass { [AssemblyInitialize] - public static int {|#0:AssemblyInitialize0|}(TestContext context) + public static int {|#0:AssemblyInitialize0|}(TestContext testContext) { return 0; } [AssemblyInitialize] - public static string {|#1:AssemblyInitialize1|}(TestContext context) + public static string {|#1:AssemblyInitialize1|}(TestContext testContext) { return "0"; } [AssemblyInitialize] - public static Task {|#2:AssemblyInitialize2|}(TestContext context) + public static async Task {|#2:AssemblyInitialize2|}(TestContext testContext) + { + await Task.Delay(0); + return 0; + } + + [AssemblyInitialize] + public static Task {|#3:AssemblyInitialize3|}(TestContext testContext) { return Task.FromResult(0); } [AssemblyInitialize] - public static ValueTask {|#3:AssemblyInitialize3|}(TestContext context) + public static async ValueTask {|#4:AssemblyInitialize4|}(TestContext testContext) + { + await Task.Delay(0); + return 0; + } + + [AssemblyInitialize] + public static ValueTask {|#5:AssemblyInitialize5|}(TestContext testContext) { return ValueTask.FromResult(0); } } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static void AssemblyInitialize0(TestContext testContext) + { + } + + [AssemblyInitialize] + public static void AssemblyInitialize1(TestContext testContext) + { + } + + [AssemblyInitialize] + public static async Task AssemblyInitialize2(TestContext testContext) + { + await Task.Delay(0); + } + + [AssemblyInitialize] + public static Task {|CS0161:AssemblyInitialize3|}(TestContext testContext) + { + } + + [AssemblyInitialize] + public static async ValueTask AssemblyInitialize4(TestContext testContext) + { + await Task.Delay(0); + } + + [AssemblyInitialize] + public static ValueTask {|CS0161:AssemblyInitialize5|}(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyInitializeShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(0) - .WithArguments("AssemblyInitialize0"), - VerifyCS.Diagnostic(AssemblyInitializeShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(1) - .WithArguments("AssemblyInitialize1"), - VerifyCS.Diagnostic(AssemblyInitializeShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(2) - .WithArguments("AssemblyInitialize2"), - VerifyCS.Diagnostic(AssemblyInitializeShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(3) - .WithArguments("AssemblyInitialize3")); + [ + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyInitialize0"), + VerifyCS.Diagnostic().WithLocation(1).WithArguments("AssemblyInitialize1"), + VerifyCS.Diagnostic().WithLocation(2).WithArguments("AssemblyInitialize2"), + VerifyCS.Diagnostic().WithLocation(3).WithArguments("AssemblyInitialize3"), + VerifyCS.Diagnostic().WithLocation(4).WithArguments("AssemblyInitialize4"), + VerifyCS.Diagnostic().WithLocation(5).WithArguments("AssemblyInitialize5") + ], + fixedCode); } public async Task WhenAssemblyInitializeReturnTypeIsValid_NoDiagnostic() @@ -272,18 +381,18 @@ public async Task WhenAssemblyInitializeReturnTypeIsValid_NoDiagnostic() public class MyTestClass { [AssemblyInitialize] - public static void AssemblyInitialize0(TestContext context) + public static void AssemblyInitialize0(TestContext testContext) { } [AssemblyInitialize] - public static Task AssemblyInitialize1(TestContext context) + public static Task AssemblyInitialize1(TestContext testContext) { return Task.CompletedTask; } [AssemblyInitialize] - public static ValueTask AssemblyInitialize2(TestContext context) + public static ValueTask AssemblyInitialize2(TestContext testContext) { return ValueTask.CompletedTask; } @@ -303,17 +412,103 @@ public async Task WhenAssemblyInitializeIsAsyncVoid_Diagnostic() public class MyTestClass { [AssemblyInitialize] - public static async void {|#0:AssemblyInitialize|}(TestContext context) + public static async void {|#0:AssemblyInitialize|}(TestContext testContext) { await Task.Delay(0); } } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static async Task AssemblyInitialize(TestContext testContext) + { + await Task.Delay(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(AssemblyInitializeShouldBeValidAnalyzer.NotAsyncVoidRule) - .WithLocation(0) - .WithArguments("AssemblyInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyInitialize"), + fixedCode); + } + + public async Task WhenMultipleViolations_TheyAllGetFixed() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public async void {|#0:AssemblyInitialize|}(int i) + { + await Task.Delay(0); + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static async Task AssemblyInitialize(TestContext testContext) + { + await Task.Delay(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyInitialize"), + fixedCode); + } + + public async Task WhenAssemblyInitializeIsNotOnClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public struct MyTestClass + { + [AssemblyInitialize] + public static void [|AssemblyInitialize|](TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenAssemblyInitializeIsOnClassNotMarkedWithTestClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class MyTestClass + { + [AssemblyInitialize] + public static void [|AssemblyInitialize|](TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldAvoidConditionalAccessAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldAvoidConditionalAccessAnalyzerTests.cs new file mode 100644 index 0000000000..f045776241 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldAvoidConditionalAccessAnalyzerTests.cs @@ -0,0 +1,333 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.AssertionArgsShouldAvoidConditionalAccessAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.UnitTests; + +[TestGroup] +public sealed class AssertionArgsShouldAvoidConditionalAccessAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + public async Task WhenUsingConditionalsAccess_In_Assert_Equal() + { + string code = """ + #nullable enable + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + public class A + { + public string? S { get; set; } + public List? T { get; set; } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + A? a = new A(); + A b = new A(); + string? s = ""; + + [|Assert.AreEqual(s?.Length, 32)|]; + [|Assert.AreEqual(((s?.Length)), 32)|]; + [|Assert.AreEqual(s?.Length, s?.Length)|]; + [|Assert.AreEqual(a?.S?.Length, 32)|]; + [|Assert.AreEqual(b.S?.Length, 32)|]; + [|Assert.AreEqual(a?.T?[3]?.Length, 32)|]; + [|Assert.AreEqual(b.T?[3]?.Length, 32)|]; + + [|Assert.AreNotEqual(s?.Length, 32)|]; + [|Assert.AreNotEqual(((s?.Length)), 32)|]; + [|Assert.AreNotEqual(s?.Length, s?.Length)|]; + [|Assert.AreNotEqual(a?.S?.Length, 32)|]; + [|Assert.AreNotEqual(b.S?.Length, 32)|]; + [|Assert.AreNotEqual(a?.T?[3]?.Length, 32)|]; + [|Assert.AreNotEqual(b.T?[3]?.Length, 32)|]; + + [|Assert.AreSame(s?.Length, 32)|]; + [|Assert.AreSame(((s?.Length)), 32)|]; + [|Assert.AreSame(s?.Length, s?.Length)|]; + [|Assert.AreSame(a?.S?.Length, 32)|]; + [|Assert.AreSame(b.S?.Length, 32)|]; + [|Assert.AreSame(a?.T?[3]?.Length, 32)|]; + [|Assert.AreSame(b.T?[3]?.Length, 32)|]; + + [|Assert.AreNotSame(s?.Length, 32)|]; + [|Assert.AreNotSame(((s?.Length)), 32)|]; + [|Assert.AreNotSame(s?.Length, s?.Length)|]; + [|Assert.AreNotSame(a?.S?.Length, 32)|]; + [|Assert.AreNotSame(b.S?.Length, 32)|]; + [|Assert.AreNotSame(a?.T?[3]?.Length, 32)|]; + [|Assert.AreNotSame(b.T?[3]?.Length, 32)|]; + } + + [TestMethod] + public void Compliant() + { + string? s = ""; + A? a = new A(); + A b = new A(); + + Assert.IsNotNull(s); + Assert.IsNotNull(a); + Assert.IsNotNull(a.S); + Assert.IsNotNull(a.T); + Assert.IsNotNull(b); + Assert.IsNotNull(b.S); + Assert.IsNotNull(b.T); + Assert.AreEqual(s.Length, 32); + Assert.AreEqual(((s.Length)), 32); + Assert.AreEqual(s.Length, s.Length); + Assert.AreEqual(a.S.Length, 32); + Assert.AreEqual(b.S.Length, 32); + Assert.AreEqual(a.T[3].Length, 32); + Assert.AreEqual(b.T[3].Length, 32); + Assert.AreNotEqual(s.Length, 32); + Assert.AreNotEqual(((s.Length)), 32); + Assert.AreNotEqual(s.Length, s.Length); + Assert.AreNotEqual(a.S.Length, 32); + Assert.AreNotEqual(b.S.Length, 32); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenUsingConditionalsAccess_In_Assert_Boolean() + { + string code = """ + #nullable enable + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + public class A + { + public string? S { get; set; } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + A? a = new A(); + A b = new A(); + string? s = ""; + + [|Assert.IsTrue(s?.Length > 32)|]; + [|Assert.IsTrue((s?.Length> 32))|]; + [|Assert.IsTrue(a?.S?.Length > 32)|]; + [|Assert.IsTrue(b.S?.Length > 32)|]; + [|Assert.IsFalse(s?.Length > 32)|]; + [|Assert.IsFalse((s?.Length > 32))|]; + [|Assert.IsFalse(a?.S?.Length > 32)|]; + [|Assert.IsFalse(b.S?.Length > 32)|]; + } + + [TestMethod] + public void Compliant() + { + string? s = ""; + A? a = new A(); + A b = new A(); + + Assert.IsNotNull(s); + Assert.IsNotNull(a); + Assert.IsNotNull(a.S); + Assert.IsNotNull(b); + Assert.IsNotNull(b.S); + Assert.IsTrue(s.Length > 32); + Assert.IsTrue((s.Length > 32)); + Assert.IsTrue(a.S.Length > 32); + Assert.IsTrue(b.S.Length > 32); + Assert.IsFalse(s.Length > 32); + Assert.IsFalse((s.Length > 32)); + Assert.IsFalse(a.S.Length > 32); + Assert.IsFalse(b.S.Length > 32); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenUsingConditionalsAccess_In_NullAssertions_Gives_NoWarning() + { + string code = """ + #nullable enable + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + public class A + { + public string? S { get; set; } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void Compliant() + { + A? a = new A(); + + Assert.IsNull(a?.S); + Assert.IsNotNull(a?.S); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenUsingConditionalsAccess_In_CollectionAssert() + { + string code = """ + #nullable enable + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + public class A + { + public List? S { get; set; } + public Type? T { get; set; } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + A? a = new A(); + A b = new A(); + + [|CollectionAssert.AreEqual(a?.S, b.S)|]; + [|CollectionAssert.AreEqual(a?.S, a?.S)|]; + [|CollectionAssert.AreEqual(b.S, a?.S)|]; + [|CollectionAssert.AreNotEqual(a?.S, b.S)|]; + [|CollectionAssert.AreNotEqual(a?.S, a?.S)|]; + [|CollectionAssert.AreNotEqual(b.S, a?.S)|]; + [|CollectionAssert.Contains(a?.S, a)|]; + [|CollectionAssert.Contains(b.S, a?.S)|]; + [|CollectionAssert.Contains(a?.S, a?.S)|]; + [|CollectionAssert.DoesNotContain(a?.S, a)|]; + [|CollectionAssert.DoesNotContain(b.S, a?.S)|]; + [|CollectionAssert.DoesNotContain(a?.S, a?.S)|]; + [|CollectionAssert.AllItemsAreNotNull(a?.S)|]; + [|CollectionAssert.AllItemsAreUnique(a?.S)|]; + [|CollectionAssert.IsSubsetOf(a?.S, b.S)|]; + [|CollectionAssert.IsSubsetOf(a?.S, a?.S)|]; + [|CollectionAssert.IsSubsetOf(b.S, a?.S)|]; + [|CollectionAssert.IsNotSubsetOf(a?.S, b.S)|]; + [|CollectionAssert.IsNotSubsetOf(a?.S, a?.S)|]; + [|CollectionAssert.IsNotSubsetOf(b.S, a?.S)|]; + [|CollectionAssert.AllItemsAreInstancesOfType(a?.S, b.T)|]; + [|CollectionAssert.AllItemsAreInstancesOfType(a?.S, a?.T)|]; + [|CollectionAssert.AllItemsAreInstancesOfType(b.S, a?.T)|]; + } + + [TestMethod] + public void Compliant() + { + A? a = new A(); + A b = new A(); + + Assert.IsNotNull(a); + CollectionAssert.AreEqual(a.S, b.S); + CollectionAssert.AreNotEqual(a.S, b.S); + CollectionAssert.Contains(a.S, a); + CollectionAssert.DoesNotContain(a.S, a); + CollectionAssert.AllItemsAreNotNull(a.S); + CollectionAssert.AllItemsAreUnique(a.S); + CollectionAssert.IsSubsetOf(a.S, b.S); + CollectionAssert.IsNotSubsetOf(a.S, b.S); + CollectionAssert.AllItemsAreInstancesOfType(a.S, a.T); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenUsingConditionalsAccess_In_StringAssert() + { + string code = """ + #nullable enable + using System.Text.RegularExpressions; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + public class A + { + public string? S { get; set; } + public Regex? R { get; set; } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + string s = ""; + Regex r = new Regex(""); + A? a = new A(); + A b = new A(); + + [|StringAssert.Contains(a?.S, s)|]; + [|StringAssert.Contains(a?.S, a?.S)|]; + [|StringAssert.Contains(b.S, a?.S)|]; + [|StringAssert.StartsWith(a?.S, s)|]; + [|StringAssert.StartsWith(a?.S, a?.S)|]; + [|StringAssert.StartsWith(s, a?.S)|]; + [|StringAssert.EndsWith(a?.S, s)|]; + [|StringAssert.EndsWith(a?.S, a?.S)|]; + [|StringAssert.EndsWith(s, a?.S)|]; + [|StringAssert.Matches(a?.S, r)|]; + [|StringAssert.Matches(a?.S, a?.R)|]; + [|StringAssert.Matches(s, a?.R)|]; + [|StringAssert.DoesNotMatch(a?.S, r)|]; + [|StringAssert.DoesNotMatch(a?.S, a?.R)|]; + [|StringAssert.DoesNotMatch(s, a?.R)|]; + } + + [TestMethod] + public void Compliant() + { + string s = ""; + Regex r = new Regex(""); + A? a = new A(); + A b = new A(); + + Assert.IsNotNull(a); + StringAssert.Contains(a.S, s); + StringAssert.Contains(a.S, a.S); + StringAssert.Contains(b.S, a.S); + StringAssert.StartsWith(a.S, s); + StringAssert.StartsWith(a.S, a.S); + StringAssert.StartsWith(s, a.S); + StringAssert.EndsWith(a.S, s); + StringAssert.EndsWith(a.S, a.S); + StringAssert.EndsWith(s, a.S); + StringAssert.Matches(a.S, r); + StringAssert.Matches(a.S, a.R); + StringAssert.Matches(s, a.R); + StringAssert.DoesNotMatch(a.S, r); + StringAssert.DoesNotMatch(a.S, a.R); + StringAssert.DoesNotMatch(s, a.R); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.cs index 0c50449d39..413be9c844 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.cs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.AssertionArgsShouldBePassedInCorrectOrderAnalyzer, - Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + MSTest.Analyzers.AssertionArgsShouldBePassedInCorrectOrderFixer>; namespace MSTest.Analyzers.UnitTests; @@ -87,7 +84,81 @@ public void Compliant() } """; - await VerifyCS.VerifyAnalyzerAsync(code); + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + string s = ""; + bool b = true; + int i = 42; + + Assert.AreEqual("", s); + Assert.AreEqual(true, b); + Assert.AreEqual(1, i); + + Assert.AreNotEqual("", s); + Assert.AreNotEqual(true, b); + Assert.AreNotEqual(1, i); + + Assert.AreSame("", s); + Assert.AreSame(true, b); + Assert.AreSame(1, i); + + Assert.AreNotSame("", s); + Assert.AreNotSame(true, b); + Assert.AreNotSame(1, i); + + Assert.AreEqual("", s, EqualityComparer.Default); + Assert.AreEqual("", s, "some message"); + Assert.AreEqual("", s, EqualityComparer.Default, "some message"); + Assert.AreEqual("", s, "some message", 1, "input"); + Assert.AreEqual("", s, EqualityComparer.Default, "some message", 1, "input"); + + Assert.AreNotEqual("", s, EqualityComparer.Default); + Assert.AreNotEqual("", s, "some message"); + Assert.AreNotEqual("", s, EqualityComparer.Default, "some message"); + Assert.AreNotEqual("", s, "some message", 1, "input"); + Assert.AreNotEqual("", s, EqualityComparer.Default, "some message", 1, "input"); + + Assert.AreSame("", s, "some message"); + Assert.AreSame("", s, "some message", 1, "input"); + } + + [TestMethod] + public void Compliant() + { + string s = ""; + bool b = true; + int i = 42; + + Assert.AreEqual("", s); + Assert.AreEqual(true, b); + Assert.AreEqual(1, i); + + Assert.AreNotEqual("", s); + Assert.AreNotEqual(true, b); + Assert.AreNotEqual(1, i); + + Assert.AreSame("", s); + Assert.AreSame(true, b); + Assert.AreSame(1, i); + + Assert.AreNotSame("", s); + Assert.AreNotSame(true, b); + Assert.AreNotSame(1, i); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + fixedCode); } public async Task LiteralUsingNamedArgument() @@ -105,7 +176,7 @@ public void NonCompliant() bool b = true; int i = 42; - [|Assert.AreEqual(actual: "", expected: s)|]; + [|Assert.AreEqual(message: "message", actual: "", expected: s)|]; [|Assert.AreEqual(actual: true, expected: b)|]; [|Assert.AreEqual(actual: 1, expected: i)|]; @@ -120,6 +191,9 @@ public void NonCompliant() [|Assert.AreNotSame(actual: "", notExpected: s)|]; [|Assert.AreNotSame(actual: true, notExpected: b)|]; [|Assert.AreNotSame(actual: 1, notExpected: i)|]; + + [|Assert.AreEqual(expected: s, "")|]; + [|Assert.AreEqual(s, actual: "")|]; } [TestMethod] @@ -129,7 +203,7 @@ public void Compliant() bool b = true; int i = 42; - Assert.AreEqual(actual: s, expected: ""); + Assert.AreEqual(message: "message", actual: s, expected: ""); Assert.AreEqual(actual: b, expected: true); Assert.AreEqual(actual: i, expected: 1); @@ -144,11 +218,78 @@ public void Compliant() Assert.AreNotSame(actual: s, notExpected: ""); Assert.AreNotSame(actual: b, notExpected: true); Assert.AreNotSame(actual: i, notExpected: 1); + + Assert.AreEqual(expected: "", s); + Assert.AreEqual("", actual: s); } } """; - await VerifyCS.VerifyAnalyzerAsync(code); + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + string s = ""; + bool b = true; + int i = 42; + + Assert.AreEqual(message: "message", actual: s, expected: ""); + Assert.AreEqual(actual: b, expected: true); + Assert.AreEqual(actual: i, expected: 1); + + Assert.AreNotEqual(actual: s, notExpected: ""); + Assert.AreNotEqual(actual: b, notExpected: true); + Assert.AreNotEqual(actual: i, notExpected: 1); + + Assert.AreSame(actual: s, expected: ""); + Assert.AreSame(actual: b, expected: true); + Assert.AreSame(actual: i, expected: 1); + + Assert.AreNotSame(actual: s, notExpected: ""); + Assert.AreNotSame(actual: b, notExpected: true); + Assert.AreNotSame(actual: i, notExpected: 1); + + Assert.AreEqual(expected: "", s); + Assert.AreEqual("", actual: s); + } + + [TestMethod] + public void Compliant() + { + string s = ""; + bool b = true; + int i = 42; + + Assert.AreEqual(message: "message", actual: s, expected: ""); + Assert.AreEqual(actual: b, expected: true); + Assert.AreEqual(actual: i, expected: 1); + + Assert.AreNotEqual(actual: s, notExpected: ""); + Assert.AreNotEqual(actual: b, notExpected: true); + Assert.AreNotEqual(actual: i, notExpected: 1); + + Assert.AreSame(actual: s, expected: ""); + Assert.AreSame(actual: b, expected: true); + Assert.AreSame(actual: i, expected: 1); + + Assert.AreNotSame(actual: s, notExpected: ""); + Assert.AreNotSame(actual: b, notExpected: true); + Assert.AreNotSame(actual: i, notExpected: 1); + + Assert.AreEqual(expected: "", s); + Assert.AreEqual("", actual: s); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + fixedCode); } public async Task ConstantValue() @@ -222,7 +363,78 @@ public void Compliant() } """; - await VerifyCS.VerifyAnalyzerAsync(code); + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + private const string constString = ""; + private const int constInt = 42; + + [TestMethod] + public void NonCompliant() + { + string s = ""; + const string localConstString = ""; + int i = 42; + const int localConstInt = 42; + + Assert.AreEqual(constString, s); + Assert.AreEqual(localConstString, s); + Assert.AreEqual(constInt, i); + Assert.AreEqual(localConstInt, i); + + Assert.AreNotEqual(constString, s); + Assert.AreNotEqual(localConstString, s); + Assert.AreNotEqual(constInt, i); + Assert.AreNotEqual(localConstInt, i); + + Assert.AreSame(constString, s); + Assert.AreSame(localConstString, s); + Assert.AreSame(constInt, i); + Assert.AreSame(localConstInt, i); + + Assert.AreNotSame(constString, s); + Assert.AreNotSame(localConstString, s); + Assert.AreNotSame(constInt, i); + Assert.AreNotSame(localConstInt, i); + } + + [TestMethod] + public void Compliant() + { + string s = ""; + const string localConstString = ""; + int i = 42; + const int localConstInt = 42; + + Assert.AreEqual(constString, s); + Assert.AreEqual(localConstString, s); + Assert.AreEqual(constInt, i); + Assert.AreEqual(localConstInt, i); + + Assert.AreNotEqual(constString, s); + Assert.AreNotEqual(localConstString, s); + Assert.AreNotEqual(constInt, i); + Assert.AreNotEqual(localConstInt, i); + + Assert.AreSame(constString, s); + Assert.AreSame(localConstString, s); + Assert.AreSame(constInt, i); + Assert.AreSame(localConstInt, i); + + Assert.AreNotSame(constString, s); + Assert.AreNotSame(localConstString, s); + Assert.AreNotSame(constInt, i); + Assert.AreNotSame(localConstInt, i); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + fixedCode); } public async Task ActualAsLocalVariableOrNot() @@ -284,7 +496,9 @@ public void Compliant() } """; - await VerifyCS.VerifyAnalyzerAsync(code); + await VerifyCS.VerifyCodeFixAsync( + code, + code); } public async Task ActualOrExpectedPrefix() @@ -386,7 +600,106 @@ public void Compliant() } """; - await VerifyCS.VerifyAnalyzerAsync(code); + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + private string _expectedString = ""; + private string ExpectedString { get; } = ""; + + [TestMethod] + public void NonCompliant() + { + string s = ""; + string actualString = ""; + string expectedString = ""; + object o = 42; + object actualObject = 42; + object expectedObject = 42; + + Assert.AreEqual(s, actualString); + Assert.AreEqual(expectedString, s); + Assert.AreEqual(expectedString, actualString); + Assert.AreEqual(_expectedString, s); + Assert.AreEqual(ExpectedString, s); + Assert.AreEqual(o, actualObject); + Assert.AreEqual(expectedObject, o); + Assert.AreEqual(expectedObject, actualObject); + + Assert.AreNotEqual(s, actualString); + Assert.AreNotEqual(expectedString, s); + Assert.AreNotEqual(expectedString, actualString); + Assert.AreNotEqual(_expectedString, s); + Assert.AreNotEqual(ExpectedString, s); + Assert.AreNotEqual(o, actualObject); + Assert.AreNotEqual(expectedObject, o); + Assert.AreNotEqual(expectedObject, actualObject); + + Assert.AreSame(s, actualString); + Assert.AreSame(expectedString, s); + Assert.AreSame(expectedString, actualString); + Assert.AreSame(_expectedString, s); + Assert.AreSame(ExpectedString, s); + Assert.AreSame(o, actualObject); + Assert.AreSame(expectedObject, o); + Assert.AreSame(expectedObject, actualObject); + + Assert.AreNotSame(s, actualString); + Assert.AreNotSame(expectedString, s); + Assert.AreNotSame(expectedString, actualString); + Assert.AreNotSame(_expectedString, s); + Assert.AreNotSame(ExpectedString, s); + Assert.AreNotSame(o, actualObject); + Assert.AreNotSame(expectedObject, o); + Assert.AreNotSame(expectedObject, actualObject); + } + + [TestMethod] + public void Compliant() + { + string s = ""; + string actualString = ""; + string expectedString = ""; + object o = 42; + object actualObject = 42; + object expectedObject = 42; + + Assert.AreEqual(expectedString, s); + Assert.AreEqual(s, actualString); + Assert.AreEqual(expectedString, actualString); + Assert.AreEqual(expectedObject, o); + Assert.AreEqual(o, actualObject); + Assert.AreEqual(expectedObject, actualObject); + + Assert.AreNotEqual(expectedString, s); + Assert.AreNotEqual(s, actualString); + Assert.AreNotEqual(expectedString, actualString); + Assert.AreNotEqual(expectedObject, o); + Assert.AreNotEqual(o, actualObject); + Assert.AreNotEqual(expectedObject, actualObject); + + Assert.AreSame(expectedString, s); + Assert.AreSame(s, actualString); + Assert.AreSame(expectedString, actualString); + Assert.AreSame(expectedObject, o); + Assert.AreSame(o, actualObject); + Assert.AreSame(expectedObject, actualObject); + + Assert.AreNotSame(expectedString, s); + Assert.AreNotSame(s, actualString); + Assert.AreNotSame(expectedString, actualString); + Assert.AreNotSame(expectedObject, o); + Assert.AreNotSame(o, actualObject); + Assert.AreNotSame(expectedObject, actualObject); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + fixedCode); } public async Task MethodCalls() @@ -429,6 +742,8 @@ public void Compliant() } """; - await VerifyCS.VerifyAnalyzerAsync(code); + await VerifyCS.VerifyCodeFixAsync( + code, + code); } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidExpectedExceptionAttributeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidExpectedExceptionAttributeAnalyzerTests.cs index f6e0374b4b..b7b9501dbb 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidExpectedExceptionAttributeAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidExpectedExceptionAttributeAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.AvoidExpectedExceptionAttributeAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/ClassCleanupShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/ClassCleanupShouldBeValidAnalyzerTests.cs index 08eb222193..6c1a83618f 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/ClassCleanupShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/ClassCleanupShouldBeValidAnalyzerTests.cs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.ClassCleanupShouldBeValidAnalyzer, - Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + MSTest.Analyzers.ClassCleanupShouldBeValidFixer>; namespace MSTest.Analyzers.Test; @@ -64,11 +61,10 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.NotAGenericClassUnlessInheritanceModeSetRule) - .WithLocation(0) - .WithArguments("ClassCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassCleanup"), + code); } public async Task WhenClassCleanupIsGenericWithoutSettingInheritanceMode_Diagnostic() @@ -86,11 +82,10 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.NotAGenericClassUnlessInheritanceModeSetRule) - .WithLocation(0) - .WithArguments("ClassCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassCleanup"), + code); } public async Task WhenClassCleanupIsNotOrdinary_Diagnostic() @@ -108,11 +103,10 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.OrdinaryRule) - .WithLocation(0) - .WithArguments("Finalize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("Finalize"), + code); } public async Task WhenClassCleanupIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() @@ -152,11 +146,25 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [assembly: DiscoverInternals] + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static void ClassCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.PublicRule) - .WithLocation(0) - .WithArguments("ClassCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassCleanup"), + fixedCode); } [Arguments("protected")] @@ -178,11 +186,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static void ClassCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.PublicRule) - .WithLocation(0) - .WithArguments("ClassCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassCleanup"), + fixedCode); } public async Task WhenClassCleanupIsGeneric_Diagnostic() @@ -200,11 +220,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static void ClassCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.NotGenericRule) - .WithLocation(0) - .WithArguments("ClassCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassCleanup"), + fixedCode); } public async Task WhenClassCleanupIsNotStatic_Diagnostic() @@ -222,11 +254,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static void ClassCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.StaticRule) - .WithLocation(0) - .WithArguments("ClassCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassCleanup"), + fixedCode); } public async Task WhenClassCleanupHasParameters_Diagnostic() @@ -244,11 +288,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static void ClassCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.NoParametersRule) - .WithLocation(0) - .WithArguments("ClassCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassCleanup"), + fixedCode); } public async Task WhenClassCleanupReturnTypeIsNotValid_Diagnostic() @@ -286,20 +342,44 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static void ClassCleanup0() + { + } + + [ClassCleanup] + public static void ClassCleanup1() + { + } + + [ClassCleanup] + public static Task {|CS0161:ClassCleanup2|}() + { + } + + [ClassCleanup] + public static ValueTask {|CS0161:ClassCleanup3|}() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(0) - .WithArguments("ClassCleanup0"), - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(1) - .WithArguments("ClassCleanup1"), - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(2) - .WithArguments("ClassCleanup2"), - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(3) - .WithArguments("ClassCleanup3")); + [ + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassCleanup0"), + VerifyCS.Diagnostic().WithLocation(1).WithArguments("ClassCleanup1"), + VerifyCS.Diagnostic().WithLocation(2).WithArguments("ClassCleanup2"), + VerifyCS.Diagnostic().WithLocation(3).WithArguments("ClassCleanup3") + ], + fixedCode); } public async Task WhenClassCleanupReturnTypeIsValid_NoDiagnostic() @@ -350,10 +430,113 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static async Task ClassCleanup() + { + await Task.Delay(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassCleanup"), + fixedCode); + } + + public async Task WhenMultipleViolations_TheyAllGetFixed() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public async void {|#0:ClassCleanup|}(int i) + { + await Task.Delay(0); + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static async Task ClassCleanup() + { + await Task.Delay(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassCleanupShouldBeValidAnalyzer.NotAsyncVoidRule) - .WithLocation(0) - .WithArguments("ClassCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassCleanup"), + fixedCode); + } + + public async Task WhenClassCleanupIsNotOnClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public struct MyTestClass + { + [ClassCleanup] + public static void [|ClassCleanup|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenClassCleanupIsOnSealedClassNotMarkedWithTestClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public sealed class MyTestClass + { + [ClassCleanup] + public static void [|ClassCleanup|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenClassCleanupIsOnNonSealedClassNotMarkedWithTestClass_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class MyTestClass + { + [ClassCleanup] + public static void ClassCleanup() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/ClassInitializeShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/ClassInitializeShouldBeValidAnalyzerTests.cs index 2dbea09c4f..9da83df98e 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/ClassInitializeShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/ClassInitializeShouldBeValidAnalyzerTests.cs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.ClassInitializeShouldBeValidAnalyzer, - Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + MSTest.Analyzers.ClassInitializeShouldBeValidFixer>; namespace MSTest.Analyzers.Test; @@ -22,7 +19,7 @@ public async Task WhenClassInitializeIsPublic_NoDiagnostic() public class MyTestClass { [ClassInitialize] - public static void ClassInitialize(TestContext context) + public static void ClassInitialize(TestContext testContext) { } } @@ -40,7 +37,7 @@ public async Task WhenClassInitializeIsGenericWithInheritanceModeSet_NoDiagnosti public class MyTestClass { [ClassInitialize(inheritanceBehavior: InheritanceBehavior.BeforeEachDerivedClass)] - public static void ClassInitialize(TestContext context) + public static void ClassInitialize(TestContext testContext) { } } @@ -58,17 +55,16 @@ public async Task WhenClassInitializeIsGenericWithInheritanceModeSetToNone_Diagn public class MyTestClass { [ClassInitialize(InheritanceBehavior.None)] - public static void {|#0:ClassInitialize|}(TestContext context) + public static void {|#0:ClassInitialize|}(TestContext testContext) { } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.NotAGenericClassUnlessInheritanceModeSetRule) - .WithLocation(0) - .WithArguments("ClassInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassInitialize"), + code); } public async Task WhenClassInitializeIsGenericWithoutSettingInheritanceMode_Diagnostic() @@ -80,31 +76,30 @@ public async Task WhenClassInitializeIsGenericWithoutSettingInheritanceMode_Diag public class MyTestClass { [ClassInitialize] - public static void {|#0:ClassInitialize|}(TestContext context) + public static void {|#0:ClassInitialize|}(TestContext testContext) { } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.NotAGenericClassUnlessInheritanceModeSetRule) - .WithLocation(0) - .WithArguments("ClassInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassInitialize"), + code); } public async Task WhenClassInitializeIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; - + [assembly: DiscoverInternals] [TestClass] internal class MyTestClass { [ClassInitialize] - public static void ClassInitialize(TestContext context) + public static void ClassInitialize(TestContext testContext) { } } @@ -124,17 +119,33 @@ public async Task WhenClassInitializeIsInternal_InsidePublicClassWithDiscoverInt public class MyTestClass { [ClassInitialize] - internal static void {|#0:ClassInitialize|}(TestContext context) + internal static void {|#0:ClassInitialize|}(TestContext testContext) { } } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [assembly: DiscoverInternals] + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public static void ClassInitialize(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.PublicRule) + VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.Rule) .WithLocation(0) - .WithArguments("ClassInitialize")); + .WithArguments("ClassInitialize"), + fixedCode); } [Arguments("protected")] @@ -150,17 +161,29 @@ public async Task WhenClassInitializeIsNotPublic_Diagnostic(string accessibility public class MyTestClass { [ClassInitialize] - {{accessibility}} static void {|#0:ClassInitialize|}(TestContext context) + {{accessibility}} static void {|#0:ClassInitialize|}(TestContext testContext) { } } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public static void ClassInitialize(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.PublicRule) - .WithLocation(0) - .WithArguments("ClassInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassInitialize"), + fixedCode); } public async Task WhenClassInitializeIsNotOrdinary_Diagnostic() @@ -178,11 +201,10 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.OrdinaryRule) - .WithLocation(0) - .WithArguments("Finalize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("Finalize"), + code); } public async Task WhenClassInitializeIsGeneric_Diagnostic() @@ -194,17 +216,29 @@ public async Task WhenClassInitializeIsGeneric_Diagnostic() public class MyTestClass { [ClassInitialize] - public static void {|#0:ClassInitialize|}(TestContext context) + public static void {|#0:ClassInitialize|}(TestContext testContext) + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public static void ClassInitialize(TestContext testContext) { } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.NotGenericRule) - .WithLocation(0) - .WithArguments("ClassInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassInitialize"), + fixedCode); } public async Task WhenClassInitializeIsNotStatic_Diagnostic() @@ -216,17 +250,29 @@ public async Task WhenClassInitializeIsNotStatic_Diagnostic() public class MyTestClass { [ClassInitialize] - public void {|#0:ClassInitialize|}(TestContext context) + public void {|#0:ClassInitialize|}(TestContext testContext) + { + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public static void ClassInitialize(TestContext testContext) { } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.StaticRule) - .WithLocation(0) - .WithArguments("ClassInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassInitialize"), + fixedCode); } public async Task WhenClassInitializeDoesNotHaveParameters_Diagnostic() @@ -244,11 +290,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public static void ClassInitialize(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.SingleContextParameterRule) - .WithLocation(0) - .WithArguments("ClassInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassInitialize"), + fixedCode); } public async Task WhenClassInitializeReturnTypeIsNotValid_Diagnostic() @@ -261,45 +319,69 @@ public async Task WhenClassInitializeReturnTypeIsNotValid_Diagnostic() public class MyTestClass { [ClassInitialize] - public static int {|#0:ClassInitialize0|}(TestContext context) + public static int {|#0:ClassInitialize0|}(TestContext testContext) { return 0; } [ClassInitialize] - public static string {|#1:ClassInitialize1|}(TestContext context) + public static string {|#1:ClassInitialize1|}(TestContext testContext) { return "0"; } [ClassInitialize] - public static Task {|#2:ClassInitialize2|}(TestContext context) + public static Task {|#2:ClassInitialize2|}(TestContext testContext) { return Task.FromResult(0); } [ClassInitialize] - public static ValueTask {|#3:ClassInitialize3|}(TestContext context) + public static ValueTask {|#3:ClassInitialize3|}(TestContext testContext) { return ValueTask.FromResult(0); } } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public static void ClassInitialize0(TestContext testContext) + { + } + + [ClassInitialize] + public static void ClassInitialize1(TestContext testContext) + { + } + + [ClassInitialize] + public static Task {|CS0161:ClassInitialize2|}(TestContext testContext) + { + } + + [ClassInitialize] + public static ValueTask {|CS0161:ClassInitialize3|}(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(0) - .WithArguments("ClassInitialize0"), - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(1) - .WithArguments("ClassInitialize1"), - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(2) - .WithArguments("ClassInitialize2"), - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(3) - .WithArguments("ClassInitialize3")); + [ + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassInitialize0"), + VerifyCS.Diagnostic().WithLocation(1).WithArguments("ClassInitialize1"), + VerifyCS.Diagnostic().WithLocation(2).WithArguments("ClassInitialize2"), + VerifyCS.Diagnostic().WithLocation(3).WithArguments("ClassInitialize3") + ], + fixedCode); } public async Task WhenClassInitializeReturnTypeIsValid_NoDiagnostic() @@ -312,18 +394,18 @@ public async Task WhenClassInitializeReturnTypeIsValid_NoDiagnostic() public class MyTestClass { [ClassInitialize] - public static void ClassInitialize0(TestContext context) + public static void ClassInitialize0(TestContext testContext) { } [ClassInitialize] - public static Task ClassInitialize1(TestContext context) + public static Task ClassInitialize1(TestContext testContext) { return Task.CompletedTask; } [ClassInitialize] - public static ValueTask ClassInitialize2(TestContext context) + public static ValueTask ClassInitialize2(TestContext testContext) { return ValueTask.CompletedTask; } @@ -343,17 +425,120 @@ public async Task WhenClassInitializeIsAsyncVoid_Diagnostic() public class MyTestClass { [ClassInitialize] - public static async void {|#0:ClassInitialize|}(TestContext context) + public static async void {|#0:ClassInitialize|}(TestContext testContext) { await Task.Delay(0); } } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public static async Task ClassInitialize(TestContext testContext) + { + await Task.Delay(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(ClassInitializeShouldBeValidAnalyzer.NotAsyncVoidRule) - .WithLocation(0) - .WithArguments("ClassInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassInitialize"), + fixedCode); + } + + public async Task WhenMultipleViolations_TheyAllGetFixed() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public async void {|#0:ClassInitialize|}(int i) + { + await Task.Delay(0); + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public static async Task ClassInitialize(TestContext testContext) + { + await Task.Delay(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassInitialize"), + fixedCode); + } + + public async Task WhenClassInitializeIsNotOnClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public struct MyTestClass + { + [ClassInitialize] + public static void [|ClassInitialize|](TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenClassInitializeIsOnSealedClassNotMarkedWithTestClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public sealed class MyTestClass + { + [ClassInitialize] + public static void [|ClassInitialize|](TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenClassInitializeIsOnNonSealedClassNotMarkedWithTestClass_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class MyTestClass + { + [ClassInitialize] + public static void ClassInitialize(TestContext testContext) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs index 7be85facf7..c273069028 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.DataRowShouldBeValidAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; @@ -542,4 +539,25 @@ public void TestSomething( await VerifyCS.VerifyAnalyzerAsync(code); } + + public async Task Issue2856_ArraysInDataRow_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [DataRow(new int[] { })] + [DataRow(new int[] { 11 })] + [DataRow(new int[] { 11, 1337, 12 })] + public void ItemsTest(int[] input) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotNegateBooleanAssertionAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotNegateBooleanAssertionAnalyzerTests.cs index 8b97d2faf2..efcad90561 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotNegateBooleanAssertionAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotNegateBooleanAssertionAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.DoNotNegateBooleanAssertionAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs index 13bf3cc372..5cb352dbce 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.DoNotStoreStaticTestContextAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseSystemDescriptionAttributeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseSystemDescriptionAttributeAnalyzerTests.cs new file mode 100644 index 0000000000..de809f42f0 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseSystemDescriptionAttributeAnalyzerTests.cs @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.DoNotUseSystemDescriptionAttributeAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.Test; + +[TestGroup] +public sealed class DoNotUseSystemDescriptionAttributeAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + public async Task WhenTestMethodHasMicrosoftDescriptionAttribute_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [Description("Description")] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenTestMethodHasSystemDescriptionAttribute_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [System.ComponentModel.Description("Description")] + public void [|MyTestMethod|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenMethodWithoutTestMethodAttribute_HasSystemDescriptionAttribute_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [System.ComponentModel.Description("Description")] + public void Method() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj b/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj index eb0e125db9..35f51a3f81 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj @@ -1,13 +1,10 @@ - + net6.0 - Exe false - false true true - true @@ -15,11 +12,6 @@ - - - - - @@ -29,19 +21,6 @@ - - - - - - PreserveNewest - - - PreserveNewest - - - Always - diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.cs index a9b4009dda..00b85ab55a 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.PreferAssertFailOverAlwaysFalseConditionsAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; @@ -13,6 +10,163 @@ namespace MSTest.Analyzers.Test; [TestGroup] public sealed class PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) { + public async Task WhenIsNullAssertion_ValueParameterIsNullable_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + #nullable enable + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int? var = null; + Assert.IsNull(var); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenIsNullAssertion_ValueParameterIsNotNullable_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + #nullable enable + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int var = 3; + [|Assert.IsNull(var)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenIsNullAssertion_ValueParameterAsPropertySymbolIsNotNullable_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + #nullable enable + [TestClass] + public class MyTestClass + { + private int var = 3; + + [TestMethod] + public void Test() + { + [|Assert.IsNull(var)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenIsNullAssertion_ValueParameterAsPropertySymbolIsNullable_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + #nullable enable + [TestClass] + public class MyTestClass + { + private int? var = 3; + + [TestMethod] + public void Test() + { + Assert.IsNull(var); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenIsNullAssertion_ValueParameterAsReferenceObjectIsNotNullable_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + #nullable enable + [TestClass] + public class TestClass + { + [TestMethod] + public void Test() + { + ObjectClass obj = new ObjectClass(); + [|Assert.IsNull(obj)|]; + } + } + + public class ObjectClass + { + + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenIsNullAssertion_ValueParameterAsReferenceObjectIsNotNullable_WithoutEnableNullable_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] + public class TestClass + { + [TestMethod] + public void Test() + { + ObjectClass obj = new ObjectClass(); + Assert.IsNull(obj); + } + } + + public class ObjectClass + { + + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenIsNullAssertion_ValueParameterAsReferenceObjectIsNullable_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + #nullable enable + [TestClass] + public class TestClass + { + [TestMethod] + public void Test() + { + ObjectClass? obj = null; + + Assert.IsNull(obj); + } + } + + public class ObjectClass + { + + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + public async Task WhenAssertIsTrueIsPassedTrue_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs index 62326d189c..a159e936ef 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.PreferConstructorOverTestInitializeAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.cs index 9c27ebfd5c..f7a573c7f4 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.PreferDisposeOverTestCleanupAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestCleanupOverDisposeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestCleanupOverDisposeAnalyzerTests.cs index 0a93c522c7..d55704879f 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestCleanupOverDisposeAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestCleanupOverDisposeAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.PreferTestCleanupOverDisposeAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestInitializeOverConstructorAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestInitializeOverConstructorAnalyzerTests.cs index 4fe6ea821b..11ed6c67ce 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestInitializeOverConstructorAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestInitializeOverConstructorAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.PreferTestInitializeOverConstructorAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs index 1f9f6a9b79..040e4af637 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs @@ -4,24 +4,17 @@ using System.Diagnostics; using Microsoft.Testing.Extensions; -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Internal.Framework.Configurations; -using Microsoft.Testing.Platform.Builder; -using Microsoft.Testing.Platform.CommandLine; -using Microsoft.Testing.Platform.Extensions; -using Microsoft.Testing.Platform.Extensions.TestHost; -using Microsoft.Testing.Platform.Services; -using Microsoft.Testing.TestInfrastructure; // DebuggerUtility.AttachVSToCurrentProcess(); ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.TestHost.AddTestApplicationLifecycleCallbacks(sp => new GlobalTasks(sp.GetCommandLineOptions())); builder.AddTestFramework(new TestFrameworkConfiguration(Debugger.IsAttached ? 1 : Environment.ProcessorCount), new MSTest.Analyzers.UnitTests.SourceGeneratedTestNodesBuilder()); #if ENABLE_CODECOVERAGE builder.AddCodeCoverageProvider(); #endif builder.AddCrashDumpProvider(); +builder.AddHangDumpProvider(); builder.AddTrxReportProvider(); // Custom suite tools @@ -30,32 +23,3 @@ builder.TestHost.AddTestSessionLifetimeHandle(slowestTestCompositeServiceFactory); ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); - -internal sealed class GlobalTasks : ITestApplicationLifecycleCallbacks -{ - private readonly ICommandLineOptions _commandLineOptions; - - public GlobalTasks(ICommandLineOptions commandLineOptions) - { - _commandLineOptions = commandLineOptions; - } - - public string Uid => nameof(GlobalTasks); - - public string Version => "1.0.0"; - - public string DisplayName => string.Empty; - - public string Description => string.Empty; - - public Task IsEnabledAsync() => Task.FromResult(true); - - public async Task AfterRunAsync(int returnValue, CancellationToken cancellationToken) - { - // Check if any tests are missing that were supposed to run. - TestsRunWatchDog.BaselineFile = Path.Combine(AppContext.BaseDirectory, "testsbaseline.txt"); - await TestsRunWatchDog.VerifyAsync(skip: _commandLineOptions.IsServerMode(), fixBaseLine: true); - } - - public Task BeforeRunAsync(CancellationToken cancellationToken) => Task.CompletedTask; -} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Properties/launchSettings.json b/test/UnitTests/MSTest.Analyzers.UnitTests/Properties/launchSettings.json index dc5d236f26..d7b9c2736a 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/Properties/launchSettings.json +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Properties/launchSettings.json @@ -5,4 +5,4 @@ "commandLineArgs": "--treenode-filter /*/*/*/**" } } -} \ No newline at end of file +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PublicClassShouldBeTestClassAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PublicClassShouldBeTestClassAnalyzerTests.cs index a37251447c..5b7ee5d0d4 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PublicClassShouldBeTestClassAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PublicClassShouldBeTestClassAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.PublicTypeShouldBeTestClassAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; @@ -13,7 +10,7 @@ namespace MSTest.Analyzers.Test; [TestGroup] public sealed class PublicClassShouldBeTestClassAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) { - public async Task WhenTypeIsPublicAndNotTestClass_Diagnostic() + public async Task WhenClassIsPublicAndNotTestClass_Diagnostic() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -21,12 +18,47 @@ public async Task WhenTypeIsPublicAndNotTestClass_Diagnostic() public class [|MyTestClass|] { } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenClassIsPublicAndNotClass_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public struct MyTestStruct + { + } - public struct [|MyTestStruct|] + public interface MyTestInterface { } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenClassIsPublicAndAbstract_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public abstract class MyTestClass + { + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenClassIsPublicAndStatic_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; - public interface [|MyTestInterface|] + public static class MyTestClass { } """; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PublicMethodShouldBeTestMethodAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PublicMethodShouldBeTestMethodAnalyzerTests.cs new file mode 100644 index 0000000000..edc1ef376d --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PublicMethodShouldBeTestMethodAnalyzerTests.cs @@ -0,0 +1,189 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.PublicMethodShouldBeTestMethodAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.Test; + +[TestGroup] +public sealed class PublicMethodShouldBeTestMethodAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + public async Task WhenMethodIsPrivate_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + private void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenMethodIsPublicButWithInvalidTestMethodSignature_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public static void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenMethodIsPublicAndMarkedAsTestMethod_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenMethodIsPrivateButNotInsideTestClass_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class MyTestClass + { + private void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenMethodIsPublicAndMarkedAsDerivedTestMethodAttribute_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class DerivedTestMethod : TestMethodAttribute + { + } + + [TestClass] + public class MyTestClass + { + [DerivedTestMethod] + public void MyTestMethod() + { + } + } + """; + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenMethodIsPublicAndNotMarkedAsTestMethodWithInheritanceFromBaseTestClass_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class Base + { + } + + public class MyTestClass : Base + { + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenMethodIsPrivateAndNotMarkedAsTestMethodWithInheritedTestClassAttribute_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class DerivedTestClass : TestClassAttribute + { + } + + [DerivedTestClass] + public class MyTestClass + { + private void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenMethodIsPublicAndNotMarkedAsTestMethod_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public void {|#0:MyTestMethod|}() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync( + code, + VerifyCS.Diagnostic(PublicMethodShouldBeTestMethodAnalyzer.PublicMethodShouldBeTestMethodRule) + .WithLocation(0) + .WithArguments("MyTestMethod")); + } + + public async Task WhenMethodIsPublicAndNotMarkedAsTestMethodWithInheritedTestClassAttribute_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class DerivedTestClass : TestClassAttribute + { + } + + [DerivedTestClass] + public class MyTestClass + { + public void {|#0:MyTestMethod|}() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync( + code, + VerifyCS.Diagnostic(PublicMethodShouldBeTestMethodAnalyzer.PublicMethodShouldBeTestMethodRule) + .WithLocation(0) + .WithArguments("MyTestMethod")); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/ReviewAlwaysTrueAssertConditionAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/ReviewAlwaysTrueAssertConditionAnalyzerTests.cs new file mode 100644 index 0000000000..3116986593 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/ReviewAlwaysTrueAssertConditionAnalyzerTests.cs @@ -0,0 +1,1115 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.Test; + +[TestGroup] +public sealed class ReviewAlwaysTrueAssertConditionAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + public async Task WhenIsNotNullAssertion_ValueParameterIsNotNullable_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + #nullable enable + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int? var = null; + Assert.IsNotNull(var); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenIsNotNullAssertion_ValueParameterIsNotNullable_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + #nullable enable + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int var = 3; + [|Assert.IsNotNull(var)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenIsNotNullAssertion_ValueParameterAsPropertySymbolIsNotNullable_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + #nullable enable + [TestClass] + public class MyTestClass + { + private int var = 3; + + [TestMethod] + public void Test() + { + [|Assert.IsNotNull(var)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenIsNotNullAssertion_ValueParameterAsPropertySymbolIsNullable_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + #nullable enable + [TestClass] + public class MyTestClass + { + private int? var = 3; + + [TestMethod] + public void Test() + { + Assert.IsNotNull(var); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenIsNotNullAssertion_ValueParameterAsReferenceObjectIsNotNullable_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + #nullable enable + [TestClass] + public class TestClass + { + [TestMethod] + public void Test() + { + ObjectClass obj = new ObjectClass(); + [|Assert.IsNotNull(obj)|]; + } + } + + public class ObjectClass + { + + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenIsNotNullAssertion_ValueParameterAsReferenceObjectIsNotNullable_WithoutEnableNullable_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] + public class TestClass + { + [TestMethod] + public void Test() + { + ObjectClass obj = new ObjectClass(); + Assert.IsNotNull(obj); + } + } + + public class ObjectClass + { + + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenIsNotNullAssertion_ValueParameterAsReferenceObjectIsNotNullable_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + #nullable enable + [TestClass] + public class TestClass + { + [TestMethod] + public void Test() + { + ObjectClass? obj = null; + + Assert.IsNotNull(obj); + } + } + + public class ObjectClass + { + + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenAssertIsFalseIsPassedTrue_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsFalse(true); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsFalseIsPassedTrue_WithMessage_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsFalse(true, "message"); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsFalseIsPassedTrue_WithMessageFirst_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsFalse(message: "message", condition: true); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsFalseIsPassedFalse_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.IsFalse(false)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsFalseIsPassedFalse_WithMessage_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.IsFalse(false, "message")|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsFalseIsPassedFalse_WithMessageFirst_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.IsFalse(message: "message", condition: false)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsFalseIsPassedUnknown_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsFalse(GetBoolean()); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsFalseIsPassedUnknown_WithMessage_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsFalse(GetBoolean(), "message"); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsFalseIsPassedUnknown_WithMessageFirst_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsFalse(message: "message", condition: GetBoolean()); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsTrueIsPassedTrue_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.IsTrue(true)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsTrueIsPassedTrue_WithMessage_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.IsTrue(true, "message")|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsTrueIsPassedTrue_WithMessageFirst_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.IsTrue(message: "message", condition: true)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsTrueIsPassedFalse_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsTrue(false); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsTrueIsPassedFalse_WithMessage_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsTrue(false, "message"); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsTrueIsPassedFalse_WithMessageFirst_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsTrue(message: "message", condition: false); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsTrueIsPassedUnknown_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsTrue(GetBoolean()); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsTrueIsPassedUnknown_WithMessage_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsTrue(GetBoolean(), "message"); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsTrueIsPassedUnknown_WithMessageFirst_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsTrue(message: "message", condition: GetBoolean()); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsNullIsPassedNull_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.IsNull(null)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsNullIsPassedNull_WithMessage_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.IsNull(null, "message")|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsNullIsPassedNull_WithMessageFirst_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.IsNull(message: "message", value: null)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsNullIsPassedUnknown_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsNull(GetObject()); + } + + private static object GetObject() => new object(); + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsNullIsPassedUnknown_WithMessage_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsNull(GetObject(), "message"); + } + + private static object GetObject() => new object(); + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertIsNullIsPassedUnknown_WithMessageFirst_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.IsNull(message: "message", value: GetObject()); + } + + private static object GetObject() => new object(); + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreNotEqualIsPassedEqual_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreNotEqual(true, true); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreNotEqualIsPassedEqual_WithMessage_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreNotEqual(true, true, "message"); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreNotEqualIsPassedEqual_WithMessageFirst_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreNotEqual(message: "message", notExpected: true, actual: true); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreNotEqualIsPassedEqual_WithMessageSecond_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreNotEqual(notExpected: true, message: "message", actual: true); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreNotEqualIsPassedNonEqual_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.AreNotEqual(true, false)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreNotEqualIsPassedNonEqual_WithMessage_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.AreNotEqual(true, false, "message")|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreNotEqualIsPassedNonEqual_WithMessageFirst_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.AreNotEqual(message: "message", notExpected: true, actual: false)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreNotEqualIsPassedNonEqual_WithMessageSecond_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.AreNotEqual(notExpected: true, message: "message", actual: false)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreNotEqualIsPassedUnknown_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreNotEqual(GetBoolean(), GetBoolean()); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreNotEqualIsPassedUnknown_WithMessage_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreNotEqual(GetBoolean(), GetBoolean(), "message"); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreNotEqualIsPassedUnknown_WithMessageFirst_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreNotEqual(message: "message", notExpected: GetBoolean(), actual: GetBoolean()); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreNotEqualIsPassedUnknown_WithMessageSecond_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreNotEqual(notExpected: GetBoolean(), message: "message", actual: GetBoolean()); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreEqualIsPassedEqual_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.AreEqual(true, true)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreEqualIsPassedEqual_WithMessage_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.AreEqual(true, true, "message")|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreEqualIsPassedEqual_WithMessageFirst_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.AreEqual(message: "message", expected: true, actual: true)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreEqualIsPassedEqual_WithMessageSecond_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.AreEqual(expected: true, message: "message", actual: true)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreEqualIsPassedNonEqual_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreEqual(true, false); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreEqualIsPassedNonEqual_WithMessage_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreEqual(true, false, "message"); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreEqualIsPassedNonEqual_WithMessageFirst_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreEqual(message: "message", expected: true, actual: false); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreEqualIsPassedNonEqual_WithMessageSecond_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreEqual(expected: true, message: "message", actual: false); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreEqualIsPassedUnknown_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreEqual(GetBoolean(), GetBoolean()); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreEqualIsPassedUnknown_WithMessage_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreEqual(GetBoolean(), GetBoolean(), "message"); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreEqualIsPassedUnknown_WithMessageFirst_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreEqual(message: "message", expected: GetBoolean(), actual: GetBoolean()); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + public async Task WhenAssertAreEqualIsPassedUnknown_WithMessageSecond_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.AreEqual(message: "message", expected: GetBoolean(), actual: GetBoolean()); + } + + private static bool GetBoolean() => true; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldBeValidAnalyzerTests.cs index f62936a84a..ff90661772 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldBeValidAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.TestClassShouldBeValidAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldHaveTestMethodAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldHaveTestMethodAnalyzerTests.cs index 2ee249c52e..495665896d 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldHaveTestMethodAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldHaveTestMethodAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.TestClassShouldHaveTestMethodAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestCleanupShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestCleanupShouldBeValidAnalyzerTests.cs index 8ecb71c680..099c19cf2f 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestCleanupShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestCleanupShouldBeValidAnalyzerTests.cs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.TestCleanupShouldBeValidAnalyzer, - Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + MSTest.Analyzers.TestCleanupShouldBeValidFixer>; namespace MSTest.Analyzers.Test; @@ -68,11 +65,27 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [assembly: DiscoverInternals] + + [TestClass] + public class MyTestClass + { + [TestCleanup] + public void TestCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.PublicRule) + VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.Rule) .WithLocation(0) - .WithArguments("TestCleanup")); + .WithArguments("TestCleanup"), + fixedCode); } [Arguments("protected")] @@ -94,11 +107,25 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestCleanup] + public void TestCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.PublicRule) + VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.Rule) .WithLocation(0) - .WithArguments("TestCleanup")); + .WithArguments("TestCleanup"), + fixedCode); } public async Task WhenTestCleanupIsNotOrdinary_Diagnostic() @@ -116,11 +143,10 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.OrdinaryRule) - .WithLocation(0) - .WithArguments("Finalize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("Finalize"), + code); } public async Task WhenTestCleanupIsAbstract_Diagnostic() @@ -136,11 +162,23 @@ public abstract class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public abstract class MyTestClass + { + [TestCleanup] + public void TestCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.NotAbstractRule) - .WithLocation(0) - .WithArguments("TestCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestCleanup"), + fixedCode); } public async Task WhenTestCleanupIsGeneric_Diagnostic() @@ -158,11 +196,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestCleanup] + public void TestCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.NotGenericRule) - .WithLocation(0) - .WithArguments("TestCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestCleanup"), + fixedCode); } public async Task WhenTestCleanupIsStatic_Diagnostic() @@ -180,11 +230,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestCleanup] + public void TestCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.NotStaticRule) - .WithLocation(0) - .WithArguments("TestCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestCleanup"), + fixedCode); } public async Task WhenTestCleanupHasParameters_Diagnostic() @@ -202,11 +264,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestCleanup] + public void TestCleanup() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.NoParametersRule) - .WithLocation(0) - .WithArguments("TestCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestCleanup"), + fixedCode); } public async Task WhenTestCleanupReturnTypeIsNotValid_Diagnostic() @@ -244,20 +318,44 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [TestCleanup] + public void TestCleanup0() + { + } + + [TestCleanup] + public void TestCleanup1() + { + } + + [TestCleanup] + public Task {|CS0161:TestCleanup2|}() + { + } + + [TestCleanup] + public ValueTask {|CS0161:TestCleanup3|}() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(0) - .WithArguments("TestCleanup0"), - VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(1) - .WithArguments("TestCleanup1"), - VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(2) - .WithArguments("TestCleanup2"), - VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(3) - .WithArguments("TestCleanup3")); + [ + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestCleanup0"), + VerifyCS.Diagnostic().WithLocation(1).WithArguments("TestCleanup1"), + VerifyCS.Diagnostic().WithLocation(2).WithArguments("TestCleanup2"), + VerifyCS.Diagnostic().WithLocation(3).WithArguments("TestCleanup3") + ], + fixedCode); } public async Task WhenTestCleanupReturnTypeIsValid_NoDiagnostic() @@ -308,10 +406,113 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [TestCleanup] + public async Task TestCleanup() + { + await Task.Delay(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestCleanupShouldBeValidAnalyzer.NotAsyncVoidRule) - .WithLocation(0) - .WithArguments("TestCleanup")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestCleanup"), + fixedCode); + } + + public async Task WhenMultipleViolations_TheyAllGetFixed() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [TestCleanup] + public static async void {|#0:TestCleanup|}(int i) + { + await Task.Delay(0); + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [TestCleanup] + public async Task TestCleanup() + { + await Task.Delay(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestCleanup"), + fixedCode); + } + + public async Task WhenTestCleanupIsNotOnClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public struct MyTestClass + { + [TestCleanup] + public void [|TestCleanup|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenTestCleanupIsOnSealedClassNotMarkedWithTestClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public sealed class MyTestClass + { + [TestCleanup] + public void [|TestCleanup|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenTestCleanupIsOnNonSealedClassNotMarkedWithTestClass_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class MyTestClass + { + [TestCleanup] + public void TestCleanup() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs index c161197fbc..6bd5a4686d 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.TestContextShouldBeValidAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestInitializeShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestInitializeShouldBeValidAnalyzerTests.cs index 76a076a0b2..a4c7a358f8 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestInitializeShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestInitializeShouldBeValidAnalyzerTests.cs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.TestInitializeShouldBeValidAnalyzer, - Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + MSTest.Analyzers.TestInitializeShouldBeValidFixer>; namespace MSTest.Analyzers.Test; @@ -46,11 +43,10 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.OrdinaryRule) - .WithLocation(0) - .WithArguments("Finalize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("Finalize"), + code); } public async Task WhenTestInitializeIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() @@ -90,11 +86,25 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [assembly: DiscoverInternals] + + [TestClass] + public class MyTestClass + { + [TestInitialize] + public void TestInitialize() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.PublicRule) - .WithLocation(0) - .WithArguments("TestInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestInitialize"), + fixedCode); } [Arguments("protected")] @@ -116,11 +126,25 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + public void TestInitialize() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.PublicRule) + VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.Rule) .WithLocation(0) - .WithArguments("TestInitialize")); + .WithArguments("TestInitialize"), + fixedCode); } public async Task WhenTestInitializeIsAbstract_Diagnostic() @@ -136,11 +160,23 @@ public abstract class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public abstract class MyTestClass + { + [TestInitialize] + public void TestInitialize() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.NotAbstractRule) - .WithLocation(0) - .WithArguments("TestInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestInitialize"), + fixedCode); } public async Task WhenTestInitializeIsGeneric_Diagnostic() @@ -158,11 +194,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + public void TestInitialize() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.NotGenericRule) - .WithLocation(0) - .WithArguments("TestInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestInitialize"), + fixedCode); } public async Task WhenTestInitializeIsStatic_Diagnostic() @@ -180,11 +228,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + public void TestInitialize() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.NotStaticRule) - .WithLocation(0) - .WithArguments("TestInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestInitialize"), + fixedCode); } public async Task WhenTestInitializeHasParameters_Diagnostic() @@ -202,11 +262,23 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + public void TestInitialize() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.NoParametersRule) - .WithLocation(0) - .WithArguments("TestInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestInitialize"), + fixedCode); } public async Task WhenTestInitializeReturnTypeIsNotValid_Diagnostic() @@ -244,20 +316,44 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + public void TestInitialize0() + { + } + + [TestInitialize] + public void TestInitialize1() + { + } + + [TestInitialize] + public Task {|CS0161:TestInitialize2|}() + { + } + + [TestInitialize] + public ValueTask {|CS0161:TestInitialize3|}() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(0) - .WithArguments("TestInitialize0"), - VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(1) - .WithArguments("TestInitialize1"), - VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(2) - .WithArguments("TestInitialize2"), - VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.ReturnTypeRule) - .WithLocation(3) - .WithArguments("TestInitialize3")); + [ + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestInitialize0"), + VerifyCS.Diagnostic().WithLocation(1).WithArguments("TestInitialize1"), + VerifyCS.Diagnostic().WithLocation(2).WithArguments("TestInitialize2"), + VerifyCS.Diagnostic().WithLocation(3).WithArguments("TestInitialize3") + ], + fixedCode); } public async Task WhenTestInitializeReturnTypeIsValid_NoDiagnostic() @@ -308,10 +404,113 @@ public class MyTestClass } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + public async Task TestInitialize() + { + await Task.Delay(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - VerifyCS.Diagnostic(TestInitializeShouldBeValidAnalyzer.NotAsyncVoidRule) - .WithLocation(0) - .WithArguments("TestInitialize")); + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestInitialize"), + fixedCode); + } + + public async Task WhenMultipleViolations_TheyAllGetFixed() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + public static async void {|#0:TestInitialize|}(int i) + { + await Task.Delay(0); + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Threading.Tasks; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + public async Task TestInitialize() + { + await Task.Delay(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.Diagnostic().WithLocation(0).WithArguments("TestInitialize"), + fixedCode); + } + + public async Task WhenTestInitializeIsNotOnClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public struct MyTestClass + { + [TestInitialize] + public void [|TestInitialize|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenTestInitializeIsOnSealedClassNotMarkedWithTestClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public sealed class MyTestClass + { + [TestInitialize] + public void [|TestInitialize|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenTestInitializeIsOnNonSealedClassNotMarkedWithTestClass_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class MyTestClass + { + [TestInitialize] + public void TestInitialize() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldBeValidAnalyzerTests.cs index 53b86d688d..450e54e5ee 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldBeValidAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.TestMethodShouldBeValidAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldNotBeIgnoredAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldNotBeIgnoredAnalyzerTests.cs index b2c4cbd372..7deb038f4a 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldNotBeIgnoredAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldNotBeIgnoredAnalyzerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.TestMethodShouldNotBeIgnoredAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs new file mode 100644 index 0000000000..cad0ffead4 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs @@ -0,0 +1,165 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.TypeContainingTestMethodShouldBeATestClassAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.Test; + +[TestGroup] +public sealed class TypeContainingTestMethodShouldBeATestClassAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + public async Task WhenTestClassHasTestMethod_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenClassWithoutTestAttribute_HaveTestMethod_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class [|MyTestClass|] + { + [TestMethod] + public void TestMethod1() {} + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenClassWithoutTestAttribute_AndWithoutTestMethods_InheritTestClassWithTestMethods_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class [|MyTestClass|] : WithTestMethods_WithoutTestClass + { + + } + + [TestClass] + public class WithTestMethods_WithoutTestClass + { + [TestMethod] + public void TestMethod1() + { + } + + [TestMethod] + public void TestMethod2() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenClassWithoutTestAttribute_AndWithTestMethods_InheritTestClass_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class Base + { + + } + + public class [|MyTestClass|] : Base + { + [TestMethod] + public void TestMethod1() + { + } + + [TestMethod] + public void TestMethod2() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenInheritedTestClassAttribute_HasInheritedTestMethodAttribute_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class DerivedTestMethod : TestMethodAttribute + { + } + + public class DerivedTestClass : TestClassAttribute + { + } + + [DerivedTestClass] + public class MyTestClass + { + [DerivedTestMethod] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenClassWithoutTestAttribute_HasInheritedTestMethodAttribute_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class DerivedTestMethod : TestMethodAttribute + { + } + + public class [|MyTestClass|] + { + [DerivedTestMethod] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenAbstractClassWithoutTestAttribute_HaveTestMethod_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public abstract class AbstractClass + { + [TestMethod] + public void TestMethod1() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestFixtureMethodSuppressorTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestFixtureMethodSuppressorTests.cs new file mode 100644 index 0000000000..c9a55fb143 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestFixtureMethodSuppressorTests.cs @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.UnitTests.UseAsyncSuffixTestFixtureMethodSuppressorTests.WarnForMissingAsyncSuffix, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.UnitTests; + +[TestGroup] +public sealed class UseAsyncSuffixTestFixtureMethodSuppressorTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + public async Task AsyncTestFixtureMethodsWithoutSuffix_DiagnosticIsSuppressed() + { + string code = @" +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class SomeClass +{ + [AssemblyInitialize] + public static async Task [|AssemblyInitialize|]() { } + + [AssemblyCleanup] + public static async Task [|AssemblyCleanup|]() { } + + [ClassInitialize] + public static async Task [|ClassInitialize|]() { } + + [ClassCleanup] + public static async Task [|ClassCleanup|]() { } + + [TestInitialize] + public async Task [|TestInitialize|]() { } + + [TestCleanup] + public async Task [|TestCleanup|]() { } +} +"; + + // Verify issues Are reported + await new VerifyCS.Test + { + TestState = { Sources = { code } }, + }.RunAsync(); + + await new TestWithSuppressor + { + TestState = { Sources = { code } }, + }.RunAsync(); + } + + public async Task AsyncTestMethodWithSuffix_NoDiagnostic() + { + string code = """ + + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class SomeClass + { + [AssemblyInitialize] + public static async Task AssemblyInitializeAsync() { } + + [AssemblyCleanup] + public static async Task AssemblyCleanupAsync() { } + + [ClassInitialize] + public static async Task ClassInitializeAsync() { } + + [ClassCleanup] + public static async Task ClassCleanupAsync() { } + + [TestInitialize] + public async Task TestInitializeAsync() { } + + [TestCleanup] + public async Task TestCleanupAsync() { } + } + + """; + + await new VerifyCS.Test + { + TestState = { Sources = { code } }, + }.RunAsync(); + } + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1038:Compiler extensions should be implemented in assemblies with compiler-provided references", Justification = "For suppression test only.")] + [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1036:Specify analyzer banned API enforcement setting", Justification = "For suppression test only.")] + [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1041:Compiler extensions should be implemented in assemblies targeting netstandard2.0", Justification = "For suppression test only.")] + public class WarnForMissingAsyncSuffix : DiagnosticAnalyzer + { + [SuppressMessage("MicrosoftCodeAnalysisDesign", "RS1017:DiagnosticId for analyzers must be a non-null constant.", Justification = "For suppression test only.")] + public static readonly DiagnosticDescriptor Rule = new(UseAsyncSuffixTestFixtureMethodSuppressor.Rule.SuppressedDiagnosticId, "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method); + } + + private void AnalyzeSymbol(SymbolAnalysisContext context) + { + var method = (IMethodSymbol)context.Symbol; + if (method.Name.EndsWith("Async", StringComparison.Ordinal)) + { + return; + } + + if (method.ReturnType.MetadataName != "Task") + { + // Not asynchronous (incomplete checking is sufficient for this test) + return; + } + + context.ReportDiagnostic(Diagnostic.Create(Rule, method.Locations[0])); + } + } + + internal sealed class TestWithSuppressor : VerifyCS.Test + { + protected override IEnumerable GetDiagnosticAnalyzers() + { + foreach (DiagnosticAnalyzer analyzer in base.GetDiagnosticAnalyzers()) + { + yield return analyzer; + } + + yield return new UseAsyncSuffixTestFixtureMethodSuppressor(); + } + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestMethodSuppressorTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestMethodSuppressorTests.cs new file mode 100644 index 0000000000..de624c2e06 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestMethodSuppressorTests.cs @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.UnitTests.UseAsyncSuffixTestMethodSuppressorTests.WarnForMissingAsyncSuffix, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.UnitTests; + +[TestGroup] +public sealed class UseAsyncSuffixTestMethodSuppressorTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + public async Task AsyncTestMethodWithoutSuffix_DiagnosticIsSuppressed() + { + string code = + """ + + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class SomeClass + { + [TestMethod] + public async Task [|TestMethod|]() { } + } + + """; + + // Verify issue is reported + await new VerifyCS.Test + { + TestState = { Sources = { code } }, + }.RunAsync(); + + await new TestWithSuppressor + { + TestState = { Sources = { code } }, + }.RunAsync(); + } + + public async Task AsyncDataTestMethodWithoutSuffix_DiagnosticIsSuppressed() + { + string code = + """ + + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class SomeClass + { + [DataTestMethod, DataRow(0)] + public async Task [|TestMethod|](int arg) { } + } + + """; + + await new VerifyCS.Test + { + TestState = { Sources = { code } }, + }.RunAsync(); + + await new TestWithSuppressor + { + TestState = { Sources = { code } }, + }.RunAsync(); + } + + public async Task AsyncTestMethodWithSuffix_NoDiagnostic() + { + string code = + """ + + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class SomeClass + { + [TestMethod] + public async Task TestMethodAsync() { } + } + + """; + + await new VerifyCS.Test + { + TestState = { Sources = { code } }, + }.RunAsync(); + } + + [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1038:Compiler extensions should be implemented in assemblies with compiler-provided references", Justification = "For suppression test only.")] + [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1036:Specify analyzer banned API enforcement setting", Justification = "For suppression test only.")] + [DiagnosticAnalyzer(LanguageNames.CSharp)] + [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1041:Compiler extensions should be implemented in assemblies targeting netstandard2.0", Justification = "For suppression test only.")] + public class WarnForMissingAsyncSuffix : DiagnosticAnalyzer + { + [SuppressMessage("MicrosoftCodeAnalysisDesign", "RS1017:DiagnosticId for analyzers must be a non-null constant.", Justification = "For suppression test only.")] + public static readonly DiagnosticDescriptor Rule = new(UseAsyncSuffixTestMethodSuppressor.Rule.SuppressedDiagnosticId, "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method); + } + + private void AnalyzeSymbol(SymbolAnalysisContext context) + { + var method = (IMethodSymbol)context.Symbol; + if (method.Name.EndsWith("Async", StringComparison.Ordinal)) + { + return; + } + + if (method.ReturnType.MetadataName != "Task") + { + // Not asynchronous (incomplete checking is sufficient for this test) + return; + } + + context.ReportDiagnostic(Diagnostic.Create(Rule, method.Locations[0])); + } + } + + internal sealed class TestWithSuppressor : VerifyCS.Test + { + protected override IEnumerable GetDiagnosticAnalyzers() + { + foreach (DiagnosticAnalyzer analyzer in base.GetDiagnosticAnalyzers()) + { + yield return analyzer; + } + + yield return new UseAsyncSuffixTestMethodSuppressor(); + } + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAttributeOnTestMethodAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAttributeOnTestMethodAnalyzerTests.cs index 15a59b5a62..f67e6fa72e 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAttributeOnTestMethodAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAttributeOnTestMethodAnalyzerTests.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.CodeAnalysis; -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.UseAttributeOnTestMethodAnalyzer, @@ -14,8 +12,8 @@ namespace MSTest.Analyzers.Test; [TestGroup] public sealed class UseAttributeOnTestMethodAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) { - private static readonly List<(DiagnosticDescriptor Rule, string AttributeUsageExample)> RuleUsageExamples = new() - { + private static readonly List<(DiagnosticDescriptor Rule, string AttributeUsageExample)> RuleUsageExamples = + [ (UseAttributeOnTestMethodAnalyzer.OwnerRule, """Owner("owner")"""), (UseAttributeOnTestMethodAnalyzer.PriorityRule, "Priority(1)"), (UseAttributeOnTestMethodAnalyzer.TestPropertyRule, """TestProperty("name", "value")"""), @@ -23,8 +21,8 @@ public sealed class UseAttributeOnTestMethodAnalyzerTests(ITestExecutionContext (UseAttributeOnTestMethodAnalyzer.DescriptionRule, """Description("description")"""), (UseAttributeOnTestMethodAnalyzer.ExpectedExceptionRule, "ExpectedException(null)"), (UseAttributeOnTestMethodAnalyzer.CssIterationRule, "CssIteration(null)"), - (UseAttributeOnTestMethodAnalyzer.CssProjectStructureRule, "CssProjectStructure(null)"), - }; + (UseAttributeOnTestMethodAnalyzer.CssProjectStructureRule, "CssProjectStructure(null)") + ]; internal static IEnumerable<(DiagnosticDescriptor Rule, string AttributeUsageExample)> GetAttributeUsageExampleAndRuleTuples() => RuleUsageExamples.Select(tuple => (tuple.Rule, tuple.AttributeUsageExample)); diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseParallelizeAttributeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseParallelizeAttributeAnalyzerTests.cs index dd44bcfbca..ddc0b5b5b8 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseParallelizeAttributeAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseParallelizeAttributeAnalyzerTests.cs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.UseParallelizeAttributeAnalyzer, - MSTest.Analyzers.UseParallelizeAttributeFixer>; + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; namespace MSTest.Analyzers.Test; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs index a1a6e19c1b..9db21ba08b 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs @@ -8,6 +8,8 @@ using Microsoft.CodeAnalysis.Testing; using Microsoft.VisualStudio.TestTools.UnitTesting; +using TestContext = Microsoft.VisualStudio.TestTools.UnitTesting.TestContext; + namespace MSTest.Analyzers.Test; public static partial class CSharpCodeFixVerifier @@ -23,7 +25,7 @@ public Test() TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(TestContext).Assembly.Location)); SolutionTransforms.Add((solution, projectId) => { - CompilationOptions compilationOptions = solution.GetProject(projectId).CompilationOptions; + CompilationOptions compilationOptions = solution.GetProject(projectId)!.CompilationOptions!; compilationOptions = compilationOptions.WithSpecificDiagnosticOptions( compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings)); solution = solution.WithProjectCompilationOptions(projectId, compilationOptions); diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs index 27057edaf3..2647b71db9 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs @@ -43,7 +43,7 @@ public static async Task VerifyCodeFixAsync(string source, string fixedSource) /// public static async Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) - => await VerifyCodeFixAsync(source, new[] { expected }, fixedSource); + => await VerifyCodeFixAsync(source, [expected], fixedSource); /// public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource) diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpVerifierHelper.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpVerifierHelper.cs index 35887f4ab9..e49981af65 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpVerifierHelper.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpVerifierHelper.cs @@ -21,7 +21,7 @@ internal static class CSharpVerifierHelper private static ImmutableDictionary GetNullableWarningsFromCompiler() { - string[] args = { "/warnaserror:nullable" }; + string[] args = ["/warnaserror:nullable"]; CSharpCommandLineArguments commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory); ImmutableDictionary nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/testsbaseline.txt b/test/UnitTests/MSTest.Analyzers.UnitTests/testsbaseline.txt deleted file mode 100644 index 61b3dc8d4e..0000000000 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/testsbaseline.txt +++ /dev/null @@ -1,372 +0,0 @@ -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanIsInsideAGenericClass_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupHasParameters_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupIsAsyncVoid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupIsGeneric_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupIsNotOrdinary_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupIsNotPublic_Diagnostic(string) (accessibility: "internal protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupIsNotPublic_Diagnostic(string) (accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupIsNotPublic_Diagnostic(string) (accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupIsNotPublic_Diagnostic(string) (accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupIsNotStatic_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupIsPublic_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupReturnTypeIsNotValid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyCleanupShouldBeValidAnalyzerTests.WhenAssemblyCleanupReturnTypeIsValid_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeDoesNotHaveParameters_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeIsAsyncVoid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeIsGeneric_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeIsInsideAGenericClass_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeIsNotOrdinary_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeIsNotPublic_Diagnostic(string) (accessibility: "internal protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeIsNotPublic_Diagnostic(string) (accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeIsNotPublic_Diagnostic(string) (accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeIsNotPublic_Diagnostic(string) (accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeIsNotStatic_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeIsPublic_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeReturnTypeIsNotValid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AssemblyInitializeShouldBeValidAnalyzerTests.WhenAssemblyInitializeReturnTypeIsValid_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.AvoidExpectedExceptionAttributeAnalyzerTests.WhenUsed_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupHasParameters_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsAsyncVoid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsGeneric_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsGenericWithInheritanceModeSet_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsGenericWithInheritanceModeSetToNone_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsGenericWithoutSettingInheritanceMode_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsNotOrdinary_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsNotPublic_Diagnostic(string) (accessibility: "internal protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsNotPublic_Diagnostic(string) (accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsNotPublic_Diagnostic(string) (accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsNotPublic_Diagnostic(string) (accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsNotStatic_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupIsPublic_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupReturnTypeIsNotValid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassCleanupShouldBeValidAnalyzerTests.WhenClassCleanupReturnTypeIsValid_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeDoesNotHaveParameters_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsAsyncVoid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsGeneric_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsGenericWithInheritanceModeSet_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsGenericWithInheritanceModeSetToNone_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsGenericWithoutSettingInheritanceMode_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsNotOrdinary_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsNotPublic_Diagnostic(string) (accessibility: "internal protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsNotPublic_Diagnostic(string) (accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsNotPublic_Diagnostic(string) (accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsNotPublic_Diagnostic(string) (accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsNotStatic_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeIsPublic_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeReturnTypeIsNotValid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeReturnTypeIsValid_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.DefaultArguments() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.Testfx_2606_NullArgumentForArray() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowHasArgumentMismatchWithTestMethod_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowHasArgumentMismatchWithTestMethod2_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowHasArgumentMismatchWithTestMethod3_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowHasNoArgsButMethodHasOneArgument_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowHasOneNullArgumentAndMethodHasNoArguments_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowHasThreeArgumentsAndMethodHasAnIntegerAndAnArrayArgument_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowHasTypeMismatchWithTestMethod_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithOneArgument_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithOneArgumentAndCharsAreAssignableToIntegers_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithOneArgumentAndIntegersAreAssignableToDoubles_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithOneArgumentAndMethodHasAPrimitiveTypeAndADefaultArgument_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithOneArgumentAndMethodHasAPrimitiveTypeAndAParamsArgument_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithOneArgumentAndMethodHasAPrimitiveTypeAndAParamsStringArgument_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithOneArgumentAndNullsAreAssignableToIntegers_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithOneArgumentAndWithDataTestMethodAttribute_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithOneArgumentAndWithDerivedTestMethodAttribute_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithThreeArguments_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithThreeArgumentsAndMethodHasAPrimitiveTypeAndAParamsArgument_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithThreeArgumentsAndMethodHasArrayArgument_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithThreeArgumentsAndMethodHasParamsArgument_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsNotSetOnATestMethod_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowPassesOneItemAndParameterExpectsArray_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DoNotNegateBooleanAssertionAnalyzerTests.WhenAssertionIsNegated_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DoNotNegateBooleanAssertionAnalyzerTests.WhenAssertionIsNotNegated_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DoNotStoreStaticTestContextAnalyzerTests.WhenAssemblyInitializeOrClassInitialize_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DoNotStoreStaticTestContextAnalyzerTests.WhenOtherTestContext_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreEqualIsPassedEqual_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreEqualIsPassedEqual_WithMessage_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreEqualIsPassedEqual_WithMessageFirst_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreEqualIsPassedEqual_WithMessageSecond_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreEqualIsPassedNonEqual_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreEqualIsPassedNonEqual_WithMessage_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreEqualIsPassedNonEqual_WithMessageFirst_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreEqualIsPassedNonEqual_WithMessageSecond_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreEqualIsPassedUnknown_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreEqualIsPassedUnknown_WithMessage_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreEqualIsPassedUnknown_WithMessageFirst_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreEqualIsPassedUnknown_WithMessageSecond_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreNotEqualIsPassedEqual_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreNotEqualIsPassedEqual_WithMessage_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreNotEqualIsPassedEqual_WithMessageFirst_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreNotEqualIsPassedEqual_WithMessageSecond_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreNotEqualIsPassedNonEqual_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreNotEqualIsPassedNonEqual_WithMessage_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreNotEqualIsPassedNonEqual_WithMessageFirst_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreNotEqualIsPassedNonEqual_WithMessageSecond_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreNotEqualIsPassedUnknown_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreNotEqualIsPassedUnknown_WithMessage_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreNotEqualIsPassedUnknown_WithMessageFirst_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertAreNotEqualIsPassedUnknown_WithMessageSecond_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsFalseIsPassedFalse_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsFalseIsPassedFalse_WithMessage_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsFalseIsPassedFalse_WithMessageFirst_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsFalseIsPassedTrue_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsFalseIsPassedTrue_WithMessage_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsFalseIsPassedTrue_WithMessageFirst_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsFalseIsPassedUnknown_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsFalseIsPassedUnknown_WithMessage_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsFalseIsPassedUnknown_WithMessageFirst_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsNotNullIsPassedNull_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsNotNullIsPassedNull_WithMessage_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsNotNullIsPassedNull_WithMessageFirst_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsNotNullIsPassedUnknown_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsNotNullIsPassedUnknown_WithMessage_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsNotNullIsPassedUnknown_WithMessageFirst_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsTrueIsPassedFalse_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsTrueIsPassedFalse_WithMessage_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsTrueIsPassedFalse_WithMessageFirst_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsTrueIsPassedTrue_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsTrueIsPassedTrue_WithMessage_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsTrueIsPassedTrue_WithMessageFirst_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsTrueIsPassedUnknown_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsTrueIsPassedUnknown_WithMessage_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.WhenAssertIsTrueIsPassedUnknown_WithMessageFirst_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferConstructorOverTestInitializeAnalyzerTests.WhenTestClassHasCtor_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferConstructorOverTestInitializeAnalyzerTests.WhenTestClassHasTestInitialize_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferConstructorOverTestInitializeAnalyzerTests.WhenTestClassHasTestInitializeAndCtor_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferConstructorOverTestInitializeAnalyzerTests.WhenTestClassHasTestInitializeAsync_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferDisposeOverTestCleanupAnalyzerTests.WhenTestClassHasDispose_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferDisposeOverTestCleanupAnalyzerTests.WhenTestClassHasDisposeAsync_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferDisposeOverTestCleanupAnalyzerTests.WhenTestClassHasTestCleanup_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferDisposeOverTestCleanupAnalyzerTests.WhenTestClassHasTestCleanupTask_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferDisposeOverTestCleanupAnalyzerTests.WhenTestClassHasTestCleanupValueTask_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferTestCleanupOverDisposeAnalyzerTests.WhenTestClassHasDispose_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferTestCleanupOverDisposeAnalyzerTests.WhenTestClassHasDisposeAsync_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferTestCleanupOverDisposeAnalyzerTests.WhenTestClassHasTestCleanup_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferTestInitializeOverConstructorAnalyzerTests.WhenTestClassHasCtor_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferTestInitializeOverConstructorAnalyzerTests.WhenTestClassHasImplicitCtor_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferTestInitializeOverConstructorAnalyzerTests.WhenTestClassHasParameterizedCtor_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PublicClassShouldBeTestClassAnalyzerTests.WhenTypeIsNotPublicAndNotTestClass_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PublicClassShouldBeTestClassAnalyzerTests.WhenTypeIsPublicAndNotTestClass_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsGeneric_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsInnerAndNotPublicTestClass_Diagnostic(string) (accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsInnerAndNotPublicTestClass_Diagnostic(string) (accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsInternalAndNotTestClass_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsInternalAndTestClass_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsPublicAndTestClass_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsPublicAndTestClassAsInnerOfInternalClass_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsStaticAndContainsAssemblyCleanup_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsStaticAndContainsAssemblyInitialize_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsStaticAndContainsClassCleanup_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsStaticAndContainsClassInitialize_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsStaticAndContainsTestCleanup_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsStaticAndContainsTestInitialize_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsStaticAndContainsTestMethod_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsStaticAndEmpty_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenDiscoverInternalsAndTypeIsInternal_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenDiscoverInternalsAndTypeIsPrivate_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenStaticTestClassWithAssemblyCleanup_DoesNotHaveTestMethod_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenStaticTestClassWithAssemblyInitialization_DoesNotHaveTestMethod_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenStaticTestClassWithoutAssemblyAttributes_DoesNotHaveTestMethod_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenTestClassDoesNotHaveTestMethod_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenTestClassHasTestMethod_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromAbstractClassHasTestMethod_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromAbstractTestClassHasTestMethod_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromBaseBaseClassHasAssemblyCleanup_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromBaseBaseClassHasTestMethod_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromClassDoesNotHaveTestMethod_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromClassHasAssemblyInitialize_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromClassHasTestMethod_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldHaveTestMethodAnalyzerTests.WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromTestClassHasTestMethod_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupHasParameters_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupIsAbstract_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupIsAsyncVoid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupIsGeneric_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupIsNotOrdinary_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupIsNotPublic_Diagnostic(string) (accessibility: "internal protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupIsNotPublic_Diagnostic(string) (accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupIsNotPublic_Diagnostic(string) (accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupIsNotPublic_Diagnostic(string) (accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupIsPublic_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupIsStatic_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupReturnTypeIsNotValid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestCleanupShouldBeValidAnalyzerTests.WhenTestCleanupReturnTypeIsValid_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenDiscoverInternalsTestContextPropertyIsPrivate_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenDiscoverInternalsTestContextPropertyIsPublicOrInternal_NoDiagnostic(string) (accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenDiscoverInternalsTestContextPropertyIsPublicOrInternal_NoDiagnostic(string) (accessibility: "public") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "testcontext", accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "TestContext", accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "TeStCoNtExT", accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "TESTCONTEXT", accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "testcontext", accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "TestContext", accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "TeStCoNtExT", accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "TESTCONTEXT", accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "testcontext", accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "TestContext", accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "TeStCoNtExT", accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "TESTCONTEXT", accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "testcontext", accessibility: "public") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "TestContext", accessibility: "public") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "TeStCoNtExT", accessibility: "public") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextCaseInsensitiveIsField_Diagnostic(string, string) (fieldName: "TESTCONTEXT", accessibility: "public") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "testcontext", accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "TestContext", accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "TeStCoNtExT", accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "TESTCONTEXT", accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "testcontext", accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "TestContext", accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "TeStCoNtExT", accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "TESTCONTEXT", accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "testcontext", accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "TestContext", accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "TeStCoNtExT", accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "TESTCONTEXT", accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "testcontext", accessibility: "public") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "TestContext", accessibility: "public") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "TeStCoNtExT", accessibility: "public") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string, string) (fieldName: "TESTCONTEXT", accessibility: "public") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextPropertyIsPrivateOrInternal_Diagnostic(string, string) (propertyName: "testcontext", accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextPropertyIsPrivateOrInternal_Diagnostic(string, string) (propertyName: "TestContext", accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextPropertyIsPrivateOrInternal_Diagnostic(string, string) (propertyName: "TeStCoNtExT", accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextPropertyIsPrivateOrInternal_Diagnostic(string, string) (propertyName: "TESTCONTEXT", accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextPropertyIsPrivateOrInternal_Diagnostic(string, string) (propertyName: "testcontext", accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextPropertyIsPrivateOrInternal_Diagnostic(string, string) (propertyName: "TestContext", accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextPropertyIsPrivateOrInternal_Diagnostic(string, string) (propertyName: "TeStCoNtExT", accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextPropertyIsPrivateOrInternal_Diagnostic(string, string) (propertyName: "TESTCONTEXT", accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextPropertyIsReadonly_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextPropertyIsStatic_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestContextShouldBeValidAnalyzerTests.WhenTestContextPropertyIsValid_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeHasParameters_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeIsAbstract_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeIsAsyncVoid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeIsGeneric_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeIsNotOrdinary_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeIsNotPublic_Diagnostic(string) (accessibility: "internal protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeIsNotPublic_Diagnostic(string) (accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeIsNotPublic_Diagnostic(string) (accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeIsNotPublic_Diagnostic(string) (accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeIsPublic_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeIsStatic_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeReturnTypeIsNotValid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestInitializeShouldBeValidAnalyzerTests.WhenTestInitializeReturnTypeIsValid_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenMethodIsNotPublicAndNotTestMethod_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodIsAbstract_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodIsAsyncVoid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodIsGeneric_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodIsInternalAndDiscoverInternals_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodIsNotOrdinary_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodIsNotPublic_Diagnostic(string) (accessibility: "internal protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodIsNotPublic_Diagnostic(string) (accessibility: "internal") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodIsNotPublic_Diagnostic(string) (accessibility: "private") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodIsNotPublic_Diagnostic(string) (accessibility: "protected") -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodIsPrivateAndDiscoverInternals_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodIsPublic_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodIsStatic_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodReturnTypeIsNotValid_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldBeValidAnalyzerTests.WhenTestMethodReturnTypeIsValid_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldNotBeIgnoredAnalyzerTests.UsingIgnoreWithoutTestMethod_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldNotBeIgnoredAnalyzerTests.WhenDerivedTestMethodAttributeIsIgnored_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldNotBeIgnoredAnalyzerTests.WhenTestMethodIsIgnored_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestMethodShouldNotBeIgnoredAnalyzerTests.WhenTestMethodIsNotIgnored_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [0] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [1] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [10] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [11] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [12] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [13] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [14] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [15] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [16] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [17] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [18] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [19] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [2] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [20] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [21] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [22] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [23] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [24] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [25] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [26] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [27] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [28] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [29] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [3] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [30] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [31] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [32] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [33] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [34] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [35] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [36] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [37] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [38] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [39] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [4] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [40] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [41] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [42] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [43] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [44] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [45] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [46] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [47] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [48] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [49] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [5] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [50] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [51] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [52] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [53] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [54] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [55] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [6] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [7] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [8] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string, Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [9] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeAndCustomTestMethod_NoDiagnosticAsync(string) [0] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeAndCustomTestMethod_NoDiagnosticAsync(string) [1] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeAndCustomTestMethod_NoDiagnosticAsync(string) [2] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeAndCustomTestMethod_NoDiagnosticAsync(string) [3] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeAndCustomTestMethod_NoDiagnosticAsync(string) [4] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeAndCustomTestMethod_NoDiagnosticAsync(string) [5] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeAndCustomTestMethod_NoDiagnosticAsync(string) [6] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeAndCustomTestMethod_NoDiagnosticAsync(string) [7] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeButNotWithTestMethod_DiagnosticAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [0] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeButNotWithTestMethod_DiagnosticAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [1] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeButNotWithTestMethod_DiagnosticAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [2] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeButNotWithTestMethod_DiagnosticAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [3] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeButNotWithTestMethod_DiagnosticAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [4] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeButNotWithTestMethod_DiagnosticAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [5] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeButNotWithTestMethod_DiagnosticAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [6] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestAttributeButNotWithTestMethod_DiagnosticAsync(Microsoft.CodeAnalysis.DiagnosticDescriptor, string) [7] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestMethodAndTestAttributes_NoDiagnosticAsync(string) [0] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestMethodAndTestAttributes_NoDiagnosticAsync(string) [1] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestMethodAndTestAttributes_NoDiagnosticAsync(string) [2] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestMethodAndTestAttributes_NoDiagnosticAsync(string) [3] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestMethodAndTestAttributes_NoDiagnosticAsync(string) [4] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestMethodAndTestAttributes_NoDiagnosticAsync(string) [5] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestMethodAndTestAttributes_NoDiagnosticAsync(string) [6] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseAttributeOnTestMethodAnalyzerTests.WhenMethodIsMarkedWithTestMethodAndTestAttributes_NoDiagnosticAsync(string) [7] -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseParallelizeAttributeAnalyzerTests.WhenDoNotParallelizeAttributeSet_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseParallelizeAttributeAnalyzerTests.WhenNoAttributeSpecified_Diagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.UseParallelizeAttributeAnalyzerTests.WhenParallelizeAttributeSet_NoDiagnostic() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.UnitTests.AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.ActualAsLocalVariableOrNot() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.UnitTests.AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.ActualOrExpectedPrefix() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.UnitTests.AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.ConstantValue() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.UnitTests.AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.LiteralUsingNamedArgument() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.UnitTests.AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.MethodCalls() -MSTest.Analyzers.UnitTests.MSTest.Analyzers.UnitTests.AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.WhenUsingLiterals() diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/AssemblyLoadWorkerTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/AssemblyLoadWorkerTests.cs index 2af496eb41..2776f4c331 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/AssemblyLoadWorkerTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/AssemblyLoadWorkerTests.cs @@ -21,7 +21,7 @@ public void GetFullPathToDependentAssembliesShouldReturnV1FrameworkAssembly() var v1AssemblyName = new AssemblyName("Microsoft.VisualStudio.QualityTools.UnitTestFramework"); var testableAssembly = new TestableAssembly { - GetReferencedAssembliesSetter = () => new AssemblyName[] { v1AssemblyName }, + GetReferencedAssembliesSetter = () => [v1AssemblyName], }; var mockAssemblyUtility = new Mock(); @@ -47,12 +47,12 @@ public void GetFullPathToDependentAssembliesShouldReturnV1FrameworkReferencedInA var dependentAssemblyName = new AssemblyName("Common.TestFramework"); var dependentAssembly = new TestableAssembly(dependentAssemblyName.Name) { - GetReferencedAssembliesSetter = () => new AssemblyName[] { v1AssemblyName }, + GetReferencedAssembliesSetter = () => [v1AssemblyName], }; var testableAssembly = new TestableAssembly { - GetReferencedAssembliesSetter = () => new AssemblyName[] { dependentAssemblyName }, + GetReferencedAssembliesSetter = () => [dependentAssemblyName], }; var mockAssemblyUtility = new Mock(); @@ -121,7 +121,7 @@ public TestableAssembly(string assemblyName) public Func FullNameSetter { get; set; } - public override AssemblyName[] GetReferencedAssemblies() => GetReferencedAssembliesSetter != null ? GetReferencedAssembliesSetter.Invoke() : Array.Empty(); + public override AssemblyName[] GetReferencedAssemblies() => GetReferencedAssembliesSetter != null ? GetReferencedAssembliesSetter.Invoke() : []; public string Name { @@ -134,7 +134,7 @@ public string Name public override string Location => Path.Combine(Path.GetDirectoryName(GetExecutingAssembly().Location), Name); - public override Module[] GetModules(bool getResourceModules) => Array.Empty(); + public override Module[] GetModules(bool getResourceModules) => []; } #endregion diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopReflectionOperationsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopReflectionOperationsTests.cs index 60bac4c1f3..3a85989d38 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopReflectionOperationsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopReflectionOperationsTests.cs @@ -31,7 +31,7 @@ public void GetCustomAttributesShouldReturnAllAttributes() Verify(attributes is not null); Verify(attributes.Length == 2); - string[] expectedAttributes = new string[] { "DummyA : base", "DummySingleA : base" }; + string[] expectedAttributes = ["DummyA : base", "DummySingleA : base"]; Verify(expectedAttributes.SequenceEqual(ReflectionUtilityTests.GetAttributeValuePairs(attributes))); } @@ -44,7 +44,7 @@ public void GetCustomAttributesOnTypeShouldReturnAllAttributes() Verify(attributes is not null); Verify(attributes.Length == 1); - string[] expectedAttributes = new string[] { "DummyA : ba" }; + string[] expectedAttributes = ["DummyA : ba"]; Verify(expectedAttributes.SequenceEqual(ReflectionUtilityTests.GetAttributeValuePairs(attributes))); } @@ -57,7 +57,7 @@ public void GetSpecificCustomAttributesOnAssemblyShouldReturnAllAttributes() Verify(attributes is not null); Verify(attributes.Length == 2); - string[] expectedAttributes = new string[] { "DummyA : a1", "DummyA : a2" }; + string[] expectedAttributes = ["DummyA : a1", "DummyA : a2"]; Verify(expectedAttributes.SequenceEqual(ReflectionUtilityTests.GetAttributeValuePairs(attributes))); } } diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs index 32f2b94ef9..245898bee6 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs @@ -139,12 +139,12 @@ private void SetupDeploymentItems(MemberInfo memberInfo, KeyValuePair[] kvpArray = new[] - { - new KeyValuePair( + KeyValuePair[] kvpArray = + [ + new KeyValuePair( DefaultDeploymentItemPath, - DefaultDeploymentItemOutputDirectory), - }; + DefaultDeploymentItemOutputDirectory) + ]; testCase.SetPropertyValue(DeploymentItemUtilityTests.DeploymentItemsProperty, kvpArray); return testCase; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceHostTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceHostTests.cs index ea907dfb09..8d734faa4a 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceHostTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceHostTests.cs @@ -125,11 +125,13 @@ public void SetupHostShouldHaveParentDomainsAppBaseSetToTestSourceLocation() // Arrange DummyClass dummyClass = new(); string runSettingxml = - @" - - False - - "; + """ + + + False + + + """; string location = typeof(TestSourceHost).Assembly.Location; var mockRunSettings = new Mock(); @@ -197,10 +199,10 @@ public void NoAppDomainShouldGetCreatedWhenDisableAppDomainIsSetToTrue() // Arrange DummyClass dummyClass = new(); string runSettingxml = - @" - - True - + @" + + True + "; string location = typeof(TestSourceHost).Assembly.Location; @@ -226,11 +228,13 @@ public void AppDomainShouldGetCreatedWhenDisableAppDomainIsSetToFalse() // Arrange DummyClass dummyClass = new(); string runSettingxml = - @" - - False - - "; + """ + + + False + + + """; string location = typeof(TestSourceHost).Assembly.Location; var mockRunSettings = new Mock(); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DiaSessionOperationsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DiaSessionOperationsTests.cs index 4adc36bb75..b0944cb9ff 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DiaSessionOperationsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DiaSessionOperationsTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !NET462 -using System.Reflection; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; @@ -58,7 +57,7 @@ public void CreateNavigationSessionShouldReturnDiaSession() typeof(MockDiaSession).AssemblyQualifiedName, typeof(MockDiaNavigationData).AssemblyQualifiedName); - object diaSession = _fileOperations.CreateNavigationSession(typeof(DiaSessionOperationsTests).GetTypeInfo().Assembly.Location); + object diaSession = _fileOperations.CreateNavigationSession(typeof(DiaSessionOperationsTests).Assembly.Location); Verify(diaSession is MockDiaSession); } @@ -78,7 +77,7 @@ public void GetNavigationDataShouldReturnDataFromNavigationSession() var navigationData = new MockDiaNavigationData() { FileName = "mock", MinLineNumber = 86 }; MockDiaSession.DiaNavigationData = navigationData; - object diaSession = _fileOperations.CreateNavigationSession(typeof(DiaSessionOperationsTests).GetTypeInfo().Assembly.Location); + object diaSession = _fileOperations.CreateNavigationSession(typeof(DiaSessionOperationsTests).Assembly.Location); _fileOperations.GetNavigationData( diaSession, typeof(DiaSessionOperationsTests).FullName, @@ -121,7 +120,7 @@ public void GetNavigationDataShouldNotThrowOnMissingFileNameField() var navigationData = new MockDiaNavigationData3() { MinLineNumber = 86 }; MockDiaSession.DiaNavigationData = navigationData; - object diaSession = _fileOperations.CreateNavigationSession(typeof(DiaSessionOperationsTests).GetTypeInfo().Assembly.Location); + object diaSession = _fileOperations.CreateNavigationSession(typeof(DiaSessionOperationsTests).Assembly.Location); _fileOperations.GetNavigationData( diaSession, typeof(DiaSessionOperationsTests).FullName, @@ -148,7 +147,7 @@ public void GetNavigationDataShouldNotThrowOnMissingLineNumberField() var navigationData = new MockDiaNavigationData2() { FileName = "mock" }; MockDiaSession.DiaNavigationData = navigationData; - object diaSession = _fileOperations.CreateNavigationSession(typeof(DiaSessionOperationsTests).GetTypeInfo().Assembly.Location); + object diaSession = _fileOperations.CreateNavigationSession(typeof(DiaSessionOperationsTests).Assembly.Location); _fileOperations.GetNavigationData( diaSession, typeof(DiaSessionOperationsTests).FullName, @@ -173,7 +172,7 @@ public void DisposeNavigationSessionShouldDisposeDiaSession() typeof(MockDiaSession).AssemblyQualifiedName, typeof(MockDiaNavigationData).AssemblyQualifiedName); - object diaSession = _fileOperations.CreateNavigationSession(typeof(DiaSessionOperationsTests).GetTypeInfo().Assembly.Location); + object diaSession = _fileOperations.CreateNavigationSession(typeof(DiaSessionOperationsTests).Assembly.Location); _fileOperations.DisposeNavigationSession(diaSession); Verify(MockDiaSession.IsDisposeInvoked); @@ -226,9 +225,7 @@ public object GetNavigationData(string className, string methodName) public void Dispose() => IsDisposeInvoked = true; } -public interface IDiaNavigationData -{ -} +public interface IDiaNavigationData; public class MockDiaNavigationData : IDiaNavigationData { diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/FileOperationsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/FileOperationsTests.cs index 691df1f10c..ea2ff03137 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/FileOperationsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/FileOperationsTests.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !NET462 -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using TestFramework.ForTestingMSTest; @@ -45,7 +43,7 @@ public void LoadAssemblyShouldThrowExceptionIfFileIsNotFound() public void LoadAssemblyShouldLoadAssemblyInCurrentContext() { - string filePath = typeof(FileOperationsTests).GetTypeInfo().Assembly.Location; + string filePath = typeof(FileOperationsTests).Assembly.Location; // This should not throw. _fileOperations.LoadAssembly(filePath, false); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestAdapterSettingsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestAdapterSettingsTests.cs index 71662ed493..9575b3a90a 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestAdapterSettingsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestAdapterSettingsTests.cs @@ -185,16 +185,18 @@ public void GetDirectoryListWithRecursivePropertyShouldReadRunSettingCorrectly() public void ToSettingsShouldNotThrowExceptionWhenRunSettingsXmlUnderTagMSTestV2IsWrong() { string runSettingsXml = - @" - true - - - - - - true - false - "; + """ + + true + + + + + + true + false + + """; StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); @@ -206,13 +208,15 @@ public void ToSettingsShouldNotThrowExceptionWhenRunSettingsXmlUnderTagMSTestV2I public void ToSettingsShouldThrowExceptionWhenRunSettingsXmlIsWrong() { string runSettingsXml = - @" - - - - - - "; + """ + + + + + + + + """; StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); @@ -231,8 +235,10 @@ public void ToSettingsShouldThrowExceptionWhenRunSettingsXmlIsWrong() public void DeploymentEnabledIsByDefaultTrueWhenNotSpecified() { string runSettingsXml = - @" - "; + """ + + + """; StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); reader.Read(); @@ -243,9 +249,11 @@ public void DeploymentEnabledIsByDefaultTrueWhenNotSpecified() public void DeploymentEnabledShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingsXml = - @" - False - "; + """ + + False + + """; StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); reader.Read(); @@ -260,8 +268,10 @@ public void DeploymentEnabledShouldBeConsumedFromRunSettingsWhenSpecified() public void DeployTestSourceDependenciesIsEnabledByDefault() { string runSettingsXml = - @" - "; + """ + + + """; StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); reader.Read(); @@ -272,9 +282,11 @@ public void DeployTestSourceDependenciesIsEnabledByDefault() public void DeployTestSourceDependenciesWhenFalse() { string runSettingsXml = - @" - False - "; + """ + + False + + """; StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); reader.Read(); @@ -285,9 +297,11 @@ public void DeployTestSourceDependenciesWhenFalse() public void DeployTestSourceDependenciesWhenTrue() { string runSettingsXml = - @" - True - "; + """ + + True + + """; StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); reader.Read(); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestSettingsProviderTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestSettingsProviderTests.cs index 91cd6a6ae0..88e25a820d 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestSettingsProviderTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestSettingsProviderTests.cs @@ -43,9 +43,11 @@ public void SettingsShouldReturnDefaultSettingsIfNotInitialized() public void SettingsShouldReturnInitializedSettings() { string runSettingxml = - @" - False - "; + """ + + False + + """; StringReader stringReader = new(runSettingxml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); reader.Read(); @@ -62,9 +64,11 @@ public void LoadShouldThrowIfReaderIsNull() public void LoadShouldReadAndFillInSettings() { string runSettingxml = - @" - False - "; + """ + + False + + """; StringReader stringReader = new(runSettingxml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); reader.Read(); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs index 89d2f6a174..6aa6052b89 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs @@ -27,7 +27,7 @@ public void GetCustomAttributesShouldReturnAllAttributes() Verify(attributes is not null); Verify(attributes.Length == 2); - string[] expectedAttributes = new string[] { "DummyA : base", "DummySingleA : base" }; + string[] expectedAttributes = ["DummyA : base", "DummySingleA : base"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -40,7 +40,7 @@ public void GetCustomAttributesShouldReturnAllAttributesIgnoringBaseInheritance( Verify(attributes is not null); Verify(attributes.Length == 2); - string[] expectedAttributes = new string[] { "DummyA : derived", "DummySingleA : derived" }; + string[] expectedAttributes = ["DummyA : derived", "DummySingleA : derived"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -54,7 +54,7 @@ public void GetCustomAttributesShouldReturnAllAttributesWithBaseInheritance() Verify(attributes.Length == 3); // Notice that the DummySingleA on the base method does not show up since it can only be defined once. - string[] expectedAttributes = new string[] { "DummyA : derived", "DummySingleA : derived", "DummyA : base", }; + string[] expectedAttributes = ["DummyA : derived", "DummySingleA : derived", "DummyA : base"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -67,7 +67,7 @@ public void GetCustomAttributesOnTypeShouldReturnAllAttributes() Verify(attributes is not null); Verify(attributes.Length == 1); - string[] expectedAttributes = new string[] { "DummyA : ba" }; + string[] expectedAttributes = ["DummyA : ba"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -80,7 +80,7 @@ public void GetCustomAttributesOnTypeShouldReturnAllAttributesIgnoringBaseInheri Verify(attributes is not null); Verify(attributes.Length == 1); - string[] expectedAttributes = new string[] { "DummyA : a" }; + string[] expectedAttributes = ["DummyA : a"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -93,7 +93,7 @@ public void GetCustomAttributesOnTypeShouldReturnAllAttributesWithBaseInheritanc Verify(attributes is not null); Verify(attributes.Length == 2); - string[] expectedAttributes = new string[] { "DummyA : a", "DummyA : ba" }; + string[] expectedAttributes = ["DummyA : a", "DummyA : ba"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -106,7 +106,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributes() Verify(attributes is not null); Verify(attributes.Length == 1); - string[] expectedAttributes = new string[] { "DummyA : base" }; + string[] expectedAttributes = ["DummyA : base"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -119,7 +119,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributesIgnoringBaseInhe Verify(attributes is not null); Verify(attributes.Length == 1); - string[] expectedAttributes = new string[] { "DummyA : derived" }; + string[] expectedAttributes = ["DummyA : derived"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -132,7 +132,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributesWithBaseInherita Verify(attributes is not null); Verify(attributes.Length == 2); - string[] expectedAttributes = new string[] { "DummyA : derived", "DummyA : base", }; + string[] expectedAttributes = ["DummyA : derived", "DummyA : base"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -145,7 +145,7 @@ public void GetSpecificCustomAttributesOnTypeShouldReturnAllAttributes() Verify(attributes is not null); Verify(attributes.Length == 1); - string[] expectedAttributes = new string[] { "DummyA : ba" }; + string[] expectedAttributes = ["DummyA : ba"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -158,7 +158,7 @@ public void GetSpecificCustomAttributesOnTypeShouldReturnAllAttributesIgnoringBa Verify(attributes is not null); Verify(attributes.Length == 1); - string[] expectedAttributes = new string[] { "DummyA : a" }; + string[] expectedAttributes = ["DummyA : a"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -171,20 +171,20 @@ public void GetSpecificCustomAttributesOnTypeShouldReturnAllAttributesWithBaseIn Verify(attributes is not null); Verify(attributes.Length == 2); - string[] expectedAttributes = new string[] { "DummyA : a", "DummyA : ba" }; + string[] expectedAttributes = ["DummyA : a", "DummyA : ba"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } public void GetSpecificCustomAttributesOnAssemblyShouldReturnAllAttributes() { - Assembly asm = typeof(DummyTestClass).GetTypeInfo().Assembly; + Assembly asm = typeof(DummyTestClass).Assembly; object[] attributes = _reflectionOperations.GetCustomAttributes(asm, typeof(DummyAAttribute)); Verify(attributes is not null); Verify(attributes.Length == 2); - string[] expectedAttributes = new string[] { "DummyA : a1", "DummyA : a2" }; + string[] expectedAttributes = ["DummyA : a1", "DummyA : a2"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -193,15 +193,13 @@ private static string[] GetAttributeValuePairs(object[] attributes) var attribValuePairs = new List(); foreach (object attrib in attributes) { - if (attrib is DummySingleAAttribute) + if (attrib is DummySingleAAttribute dummySingleAAttribute) { - var a = attrib as DummySingleAAttribute; - attribValuePairs.Add("DummySingleA : " + a.Value); + attribValuePairs.Add("DummySingleA : " + dummySingleAAttribute.Value); } - else if (attrib is DummyAAttribute) + else if (attrib is DummyAAttribute dummyAAttribute) { - var a = attrib as DummyAAttribute; - attribValuePairs.Add("DummyA : " + a.Value); + attribValuePairs.Add("DummyA : " + dummyAAttribute.Value); } } @@ -219,7 +217,7 @@ public DummyAAttribute(string foo) public string Value { get; set; } } - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)] public class DummySingleAAttribute : Attribute { public DummySingleAAttribute(string foo) diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs index 1822b8ce88..4f494f5e9f 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Reflection; @@ -59,18 +59,18 @@ public void GetDeploymentItemsReturnsDeploymentItems() var testDeployment = new TestDeployment(new DeploymentItemUtility(_mockReflectionUtility.Object), null, null); // setup mocks - KeyValuePair[] methodLevelDeploymentItems = new[] - { + KeyValuePair[] methodLevelDeploymentItems = + [ new KeyValuePair( DefaultDeploymentItemPath, - DefaultDeploymentItemOutputDirectory), - }; - KeyValuePair[] classLevelDeploymentItems = new[] - { + DefaultDeploymentItemOutputDirectory) + ]; + KeyValuePair[] classLevelDeploymentItems = + [ new KeyValuePair( DefaultDeploymentItemPath + "\\temp2", - DefaultDeploymentItemOutputDirectory), - }; + DefaultDeploymentItemOutputDirectory) + ]; MethodInfo memberInfo = typeof(TestDeploymentTests).GetMethod( "GetDeploymentItemsReturnsDeploymentItems"); @@ -116,7 +116,7 @@ public void CleanupShouldNotDeleteDirectoriesIfRunSettingsSpecifiesSo() MSTestSettingsProvider mstestSettingsProvider = new(); mstestSettingsProvider.Load(reader); - TestCase testCase = GetTestCase(typeof(TestDeploymentTests).GetTypeInfo().Assembly.Location); + TestCase testCase = GetTestCase(typeof(TestDeploymentTests).Assembly.Location); // Setup mocks. TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories); @@ -133,7 +133,7 @@ public void CleanupShouldNotDeleteDirectoriesIfRunSettingsSpecifiesSo() public void CleanupShouldDeleteRootDeploymentDirectory() { - TestCase testCase = GetTestCase(typeof(DeploymentUtilityTests).GetTypeInfo().Assembly.Location); + TestCase testCase = GetTestCase(typeof(DeploymentUtilityTests).Assembly.Location); // Setup mocks. TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories); @@ -157,7 +157,7 @@ public void CleanupShouldDeleteRootDeploymentDirectory() public void GetDeploymentDirectoryShouldReturnDeploymentOutputDirectory() { - TestCase testCase = GetTestCase(typeof(TestDeploymentTests).GetTypeInfo().Assembly.Location); + TestCase testCase = GetTestCase(typeof(TestDeploymentTests).Assembly.Location); // Setup mocks. TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories); @@ -178,12 +178,12 @@ public void GetDeploymentDirectoryShouldReturnDeploymentOutputDirectory() public void DeployShouldReturnFalseWhenDeploymentEnabledSetToFalseButHasDeploymentItems() { var testCase = new TestCase("A.C.M", new System.Uri("executor://testExecutor"), "A"); - KeyValuePair[] kvpArray = new[] - { + KeyValuePair[] kvpArray = + [ new KeyValuePair( DefaultDeploymentItemPath, - DefaultDeploymentItemOutputDirectory), - }; + DefaultDeploymentItemOutputDirectory) + ]; testCase.SetPropertyValue(DeploymentItemUtilityTests.DeploymentItemsProperty, kvpArray); var testDeployment = new TestDeployment( @@ -252,16 +252,15 @@ public void DeployShouldReturnFalseWhenDeploymentEnabledSetToTrueButHasNoDeploym } // TODO: This test has to have mocks. It actually deploys stuff and we cannot assume that all the dependencies get copied over to bin\debug. - [Ignore] - public void DeployShouldReturnTrueWhenDeploymentEnabledSetToTrueAndHasDeploymentItems() + internal void DeployShouldReturnTrueWhenDeploymentEnabledSetToTrueAndHasDeploymentItems() { - var testCase = new TestCase("A.C.M", new System.Uri("executor://testExecutor"), typeof(TestDeploymentTests).GetTypeInfo().Assembly.Location); - KeyValuePair[] kvpArray = new[] - { - new KeyValuePair( + var testCase = new TestCase("A.C.M", new System.Uri("executor://testExecutor"), typeof(TestDeploymentTests).Assembly.Location); + KeyValuePair[] kvpArray = + [ + new KeyValuePair( DefaultDeploymentItemPath, - DefaultDeploymentItemOutputDirectory), - }; + DefaultDeploymentItemOutputDirectory) + ]; testCase.SetPropertyValue(DeploymentItemUtilityTests.DeploymentItemsProperty, kvpArray); var testDeployment = new TestDeployment( new DeploymentItemUtility(_mockReflectionUtility.Object), @@ -289,9 +288,9 @@ public void DeployShouldReturnTrueWhenDeploymentEnabledSetToTrueAndHasDeployment public void GetDeploymentInformationShouldReturnAppBaseDirectoryIfRunDirectoryIsNull() { TestDeployment.Reset(); - IDictionary properties = TestDeployment.GetDeploymentInformation(typeof(TestDeploymentTests).GetTypeInfo().Assembly.Location); + IDictionary properties = TestDeployment.GetDeploymentInformation(typeof(TestDeploymentTests).Assembly.Location); - string applicationBaseDirectory = Path.GetDirectoryName(typeof(TestDeploymentTests).GetTypeInfo().Assembly.Location); + string applicationBaseDirectory = Path.GetDirectoryName(typeof(TestDeploymentTests).Assembly.Location); var expectedProperties = new Dictionary { [TestContext.TestRunDirectoryLabel] = applicationBaseDirectory, @@ -312,7 +311,7 @@ public void GetDeploymentInformationShouldReturnAppBaseDirectoryIfRunDirectoryIs public void GetDeploymentInformationShouldReturnRunDirectoryInformationIfSourceIsNull() { // Arrange. - TestCase testCase = GetTestCase(typeof(TestDeploymentTests).GetTypeInfo().Assembly.Location); + TestCase testCase = GetTestCase(typeof(TestDeploymentTests).Assembly.Location); // Setup mocks. TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories); @@ -347,7 +346,7 @@ public void GetDeploymentInformationShouldReturnRunDirectoryInformationIfSourceI public void GetDeploymentInformationShouldReturnRunDirectoryInformationIfSourceIsNotNull() { // Arrange. - TestCase testCase = GetTestCase(typeof(TestDeploymentTests).GetTypeInfo().Assembly.Location); + TestCase testCase = GetTestCase(typeof(TestDeploymentTests).Assembly.Location); // Setup mocks. TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories); @@ -358,7 +357,7 @@ public void GetDeploymentInformationShouldReturnRunDirectoryInformationIfSourceI Verify(testDeployment.Deploy(new List { testCase }, mockRunContext.Object, new Mock().Object)); // Act. - IDictionary properties = TestDeployment.GetDeploymentInformation(typeof(TestDeploymentTests).GetTypeInfo().Assembly.Location); + IDictionary properties = TestDeployment.GetDeploymentInformation(typeof(TestDeploymentTests).Assembly.Location); // Assert. var expectedProperties = new Dictionary @@ -402,12 +401,12 @@ private void SetupDeploymentItems(MemberInfo memberInfo, KeyValuePair[] kvpArray = new[] - { + KeyValuePair[] kvpArray = + [ new KeyValuePair( DefaultDeploymentItemPath, - DefaultDeploymentItemOutputDirectory), - }; + DefaultDeploymentItemOutputDirectory) + ]; testCase.SetPropertyValue(DeploymentItemUtilityTests.DeploymentItemsProperty, kvpArray); return testCase; @@ -415,7 +414,7 @@ private static TestCase GetTestCase(string source) private TestDeployment CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories) { - string currentExecutingFolder = Path.GetDirectoryName(typeof(TestDeploymentTests).GetTypeInfo().Assembly.Location); + string currentExecutingFolder = Path.GetDirectoryName(typeof(TestDeploymentTests).Assembly.Location); testRunDirectories = new TestRunDirectories(currentExecutingFolder); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ThreadSafeStringWriterTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ThreadSafeStringWriterTests.cs index 61722fa6a7..69ae52a851 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ThreadSafeStringWriterTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ThreadSafeStringWriterTests.cs @@ -109,10 +109,10 @@ public void ThreadSafeStringWriterWritesLinesIntoDifferentWritesSeparately() Verify(!string.IsNullOrWhiteSpace(result.Debug)); string[] output = result.Out.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); - Verify(output.SequenceEqual(new[] { "out", "out" })); + Verify(output.SequenceEqual(["out", "out"])); string[] debug = result.Debug.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); - Verify(debug.SequenceEqual(new[] { "debug", "debug" })); + Verify(debug.SequenceEqual(["debug", "debug"])); } } } diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/AppDomainUtilitiesTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/AppDomainUtilitiesTests.cs index 152900a9f8..0456a3cba3 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/AppDomainUtilitiesTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/AppDomainUtilitiesTests.cs @@ -46,12 +46,21 @@ public void SetConfigurationFileShouldSetOMRedirectionIfConfigFileIsPresent() Verify(configFile == setup.ConfigurationFile); // Assert Config Bytes. - string expectedRedir = ""; + string expectedRedir = + """ + + + + """; byte[] observedConfigBytes = setup.GetConfigurationBytes(); string observedXml = System.Text.Encoding.UTF8.GetString(observedConfigBytes); - Verify(observedXml.Replace("\r\n", string.Empty).Replace(" ", string.Empty).Contains(expectedRedir.Replace(" ", string.Empty)), "Config must have OM redirection"); + Verify(SanitizeString(observedXml).Contains(SanitizeString(expectedRedir)), "Config must have OM redirection"); + + // Local functions + static string SanitizeString(string str) + => str.Replace("\r\n", string.Empty).Replace(" ", string.Empty); } public void SetConfigurationFileShouldSetToCurrentDomainsConfigFileIfSourceDoesNotHaveAConfig() diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs index 96649c8cd5..5a458aa652 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs @@ -205,7 +205,6 @@ public void DeployShouldDeploySatelliteAssemblies() public void DeployShouldNotDeployIfOutputDirectoryIsInvalid() { - string assemblyFullPath = Assembly.GetExecutingAssembly().Location; string deploymentItemPath = "C:\\temp\\sample.dll"; string deploymentItemOutputDirectory = "..\\..\\out"; @@ -258,8 +257,6 @@ public void DeployShouldNotDeployIfOutputDirectoryIsInvalid() public void DeployShouldDeployContentsOfADirectoryIfSpecified() { - string assemblyFullPath = Assembly.GetExecutingAssembly().Location; - TestCase testCase = GetTestCaseAndTestRunDirectories(DefaultDeploymentItemPath, DefaultDeploymentItemOutputDirectory, out TestRunDirectories testRunDirectories); string content1 = Path.Combine(DefaultDeploymentItemPath, "directoryContents.dll"); var directoryContentFiles = new List { content1 }; @@ -468,12 +465,12 @@ public void CreateDeploymentDirectoriesShouldCreateDefaultDeploymentDirectoryIfR private static TestCase GetTestCaseAndTestRunDirectories(string deploymentItemPath, string defaultDeploymentItemOutputDirectoryOut, out TestRunDirectories testRunDirectories) { var testCase = new TestCase("A.C.M", new Uri("executor://testExecutor"), Assembly.GetExecutingAssembly().Location); - KeyValuePair[] kvpArray = new[] - { + KeyValuePair[] kvpArray = + [ new KeyValuePair( deploymentItemPath, - defaultDeploymentItemOutputDirectoryOut), - }; + defaultDeploymentItemOutputDirectoryOut) + ]; testCase.SetPropertyValue(DeploymentItemUtilityTests.DeploymentItemsProperty, kvpArray); string currentExecutingFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DesktopReflectionUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DesktopReflectionUtilityTests.cs index 9bd691ff88..562b57f372 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DesktopReflectionUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DesktopReflectionUtilityTests.cs @@ -17,14 +17,14 @@ public class ReflectionUtilityTests : TestContainer { public void GetSpecificCustomAttributesOnAssemblyShouldReturnAllAttributes() { - Assembly asm = typeof(DummyTestClass).GetTypeInfo().Assembly; + Assembly asm = typeof(DummyTestClass).Assembly; List attributes = ReflectionUtility.GetCustomAttributes(asm, typeof(DummyAAttribute)); Verify(attributes is not null); Verify(attributes.Count == 2); - string[] expectedAttributes = new string[] { "DummyA : a1", "DummyA : a2" }; + string[] expectedAttributes = ["DummyA : a1", "DummyA : a2"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns10FileUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns10FileUtilityTests.cs index 233b3f2917..0f561130a8 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns10FileUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns10FileUtilityTests.cs @@ -35,10 +35,10 @@ public void ReplaceInvalidFileNameCharactersShouldReplaceInvalidChars() public void AddFilesInADirectoryShouldReturnAllTopLevelFilesInADirectory() { - string[] topLevelFiles = new string[] { "tick.txt", "tock.tick.txt" }; + string[] topLevelFiles = ["tick.txt", "tock.tick.txt"]; _fileUtility.Setup(fu => fu.GetFilesInADirectory(It.IsAny())).Returns(topLevelFiles); - _fileUtility.Setup(fu => fu.GetDirectoriesInADirectory(It.IsAny())).Returns(Array.Empty()); + _fileUtility.Setup(fu => fu.GetDirectoriesInADirectory(It.IsAny())).Returns([]); List files = _fileUtility.Object.AddFilesFromDirectory("C:\\randomclock", false); @@ -47,13 +47,16 @@ public void AddFilesInADirectoryShouldReturnAllTopLevelFilesInADirectory() public void AddFilesInADirectoryShouldReturnAllFilesUnderSubFolders() { - string[] allFiles = new string[] - { - "MainClock\\tickmain.txt", "MainClock\\tock.tick.txt", - "MainClock\\Folder1\\tick.txt", "MainClock\\Folder1\\tock.tick.txt", - "MainClock\\Folder2\\newtick.log", "MainClock\\Folder2\\newtock.log", - "MainClock\\Folder2\\backup\\newtock.tick.txt", - }; + string[] allFiles = + [ + "MainClock\\tickmain.txt", + "MainClock\\tock.tick.txt", + "MainClock\\Folder1\\tick.txt", + "MainClock\\Folder1\\tock.tick.txt", + "MainClock\\Folder2\\newtick.log", + "MainClock\\Folder2\\newtock.log", + "MainClock\\Folder2\\backup\\newtock.tick.txt" + ]; SetupMockFileAPIs(allFiles); @@ -64,13 +67,16 @@ public void AddFilesInADirectoryShouldReturnAllFilesUnderSubFolders() public void AddFilesInADirectoryShouldReturnAllFilesUnderSubFoldersEvenIfAFolderIsEmpty() { - string[] allFiles = new string[] - { - "MainClock\\tickmain.txt", "MainClock\\tock.tick.txt", - "MainClock\\Folder1\\tick.txt", "MainClock\\Folder1\\tock.tick.txt", - "MainClock\\Folder2\\newtick.log", "MainClock\\Folder2\\newtock.log", - "MainClock\\Folder2\\backup\\", - }; + string[] allFiles = + [ + "MainClock\\tickmain.txt", + "MainClock\\tock.tick.txt", + "MainClock\\Folder1\\tick.txt", + "MainClock\\Folder1\\tock.tick.txt", + "MainClock\\Folder2\\newtick.log", + "MainClock\\Folder2\\newtock.log", + "MainClock\\Folder2\\backup\\" + ]; SetupMockFileAPIs(allFiles); @@ -85,13 +91,16 @@ public void AddFilesInADirectoryShouldReturnAllFilesUnderSubFoldersEvenIfAFolder public void AddFilesWithIgnoreDirectory() { // Setup - string[] allFiles = new string[] - { - "c:\\MainClock\\Results\\tickmain.trx", "c:\\MainClock\\Results\\Run1\\tock.tick.txt", - "c:\\MainClock\\tickmain.txt", "c:\\MainClock\\tock.tick.txt", - "c:\\MainClock\\Folder1\\tick.txt", "c:\\MainClock\\Folder1\\tock.tick.txt", - "c:\\MainClock\\Folder2\\backup\\Data.csv", - }; + string[] allFiles = + [ + "c:\\MainClock\\Results\\tickmain.trx", + "c:\\MainClock\\Results\\Run1\\tock.tick.txt", + "c:\\MainClock\\tickmain.txt", + "c:\\MainClock\\tock.tick.txt", + "c:\\MainClock\\Folder1\\tick.txt", + "c:\\MainClock\\Folder1\\tock.tick.txt", + "c:\\MainClock\\Folder2\\backup\\Data.csv" + ]; _fileUtility.Setup(fu => fu.GetDirectoriesInADirectory(It.IsAny())).Returns((directory) => { @@ -122,13 +131,16 @@ public void AddFilesWithIgnoreDirectory() public void AddFilesWithNoIgnoreDirectory() { // Setup - string[] allFiles = new string[] - { - "c:\\MainClock\\Results\\tickmain.trx", "c:\\MainClock\\Results\\Run1\\tock.tick.txt", - "c:\\MainClock\\tickmain.txt", "c:\\MainClock\\tock.tick.txt", - "c:\\MainClock\\Folder1\\tick.txt", "c:\\MainClock\\Folder1\\tock.tick.txt", - "c:\\MainClock\\Folder2\\backup\\Data.csv", - }; + string[] allFiles = + [ + "c:\\MainClock\\Results\\tickmain.trx", + "c:\\MainClock\\Results\\Run1\\tock.tick.txt", + "c:\\MainClock\\tickmain.txt", + "c:\\MainClock\\tock.tick.txt", + "c:\\MainClock\\Folder1\\tick.txt", + "c:\\MainClock\\Folder1\\tock.tick.txt", + "c:\\MainClock\\Folder2\\backup\\Data.csv" + ]; _fileUtility.Setup(fu => fu.GetDirectoriesInADirectory(It.IsAny())).Returns((directory) => { diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13DeploymentItemUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13DeploymentItemUtilityTests.cs index 26a2fdbf31..b7c2e69eda 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13DeploymentItemUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13DeploymentItemUtilityTests.cs @@ -55,12 +55,12 @@ public void GetClassLevelDeploymentItemsShouldReturnEmptyListWhenNoDeploymentIte public void GetClassLevelDeploymentItemsShouldReturnADeploymentItem() { - KeyValuePair[] kvpArray = new[] - { + KeyValuePair[] kvpArray = + [ new KeyValuePair( _defaultDeploymentItemPath, - _defaultDeploymentItemOutputDirectory), - }; + _defaultDeploymentItemOutputDirectory) + ]; SetupDeploymentItems(typeof(DeploymentItemUtilityTests).GetTypeInfo(), kvpArray); IList deploymentItems = _deploymentItemUtility.GetClassLevelDeploymentItems(typeof(DeploymentItemUtilityTests), _warnings); @@ -75,15 +75,15 @@ public void GetClassLevelDeploymentItemsShouldReturnADeploymentItem() public void GetClassLevelDeploymentItemsShouldReturnMoreThanOneDeploymentItems() { - KeyValuePair[] deploymentItemAttributes = new[] - { + KeyValuePair[] deploymentItemAttributes = + [ new KeyValuePair( _defaultDeploymentItemPath, _defaultDeploymentItemOutputDirectory), new KeyValuePair( _defaultDeploymentItemPath + "\\temp2", - _defaultDeploymentItemOutputDirectory), - }; + _defaultDeploymentItemOutputDirectory) + ]; SetupDeploymentItems(typeof(DeploymentItemUtilityTests).GetTypeInfo(), deploymentItemAttributes); IList deploymentItems = @@ -106,15 +106,15 @@ public void GetClassLevelDeploymentItemsShouldReturnMoreThanOneDeploymentItems() public void GetClassLevelDeploymentItemsShouldNotReturnDuplicateDeploymentItemEntries() { - KeyValuePair[] deploymentItemAttributes = new[] - { + KeyValuePair[] deploymentItemAttributes = + [ new KeyValuePair( _defaultDeploymentItemPath, _defaultDeploymentItemOutputDirectory), new KeyValuePair( _defaultDeploymentItemPath, - _defaultDeploymentItemOutputDirectory), - }; + _defaultDeploymentItemOutputDirectory) + ]; SetupDeploymentItems(typeof(DeploymentItemUtilityTests).GetTypeInfo(), deploymentItemAttributes); IList deploymentItems = @@ -122,27 +122,27 @@ public void GetClassLevelDeploymentItemsShouldNotReturnDuplicateDeploymentItemEn typeof(DeploymentItemUtilityTests), _warnings); - DeploymentItem[] expectedDeploymentItems = new[] - { + DeploymentItem[] expectedDeploymentItems = + [ new DeploymentItem( _defaultDeploymentItemPath, - _defaultDeploymentItemOutputDirectory), - }; + _defaultDeploymentItemOutputDirectory) + ]; Verify(expectedDeploymentItems.SequenceEqual(deploymentItems.ToArray())); } public void GetClassLevelDeploymentItemsShouldReportWarningsForInvalidDeploymentItems() { - KeyValuePair[] deploymentItemAttributes = new[] - { + KeyValuePair[] deploymentItemAttributes = + [ new KeyValuePair( _defaultDeploymentItemPath, _defaultDeploymentItemOutputDirectory), new KeyValuePair( null, - _defaultDeploymentItemOutputDirectory), - }; + _defaultDeploymentItemOutputDirectory) + ]; SetupDeploymentItems(typeof(DeploymentItemUtilityTests).GetTypeInfo(), deploymentItemAttributes); IList deploymentItems = _deploymentItemUtility.GetClassLevelDeploymentItems(typeof(DeploymentItemUtilityTests), _warnings); @@ -174,15 +174,15 @@ public void GetDeploymentItemsShouldReturnNullOnNoDeploymentItems() public void GetDeploymentItemsShouldReturnMethodLevelDeploymentItemsOnly() { - KeyValuePair[] deploymentItemAttributes = new[] - { + KeyValuePair[] deploymentItemAttributes = + [ new KeyValuePair( _defaultDeploymentItemPath, _defaultDeploymentItemOutputDirectory), new KeyValuePair( _defaultDeploymentItemPath + "\\temp2", - _defaultDeploymentItemOutputDirectory), - }; + _defaultDeploymentItemOutputDirectory) + ]; MethodInfo memberInfo = typeof(DeploymentItemUtilityTests).GetMethod( "GetDeploymentItemsShouldReturnNullOnNoDeploymentItems"); @@ -218,15 +218,15 @@ public void GetDeploymentItemsShouldReturnClassLevelDeploymentItemsOnly() KeyValuePair[] deploymentItems = _deploymentItemUtility.GetDeploymentItems(method, classLevelDeploymentItems, _warnings); // Assert. - KeyValuePair[] expectedDeploymentItems = new[] - { + KeyValuePair[] expectedDeploymentItems = + [ new KeyValuePair( _defaultDeploymentItemPath, _defaultDeploymentItemOutputDirectory), new KeyValuePair( _defaultDeploymentItemPath + "\\temp2", - _defaultDeploymentItemOutputDirectory), - }; + _defaultDeploymentItemOutputDirectory) + ]; Verify(expectedDeploymentItems.SequenceEqual(deploymentItems.ToArray())); } @@ -234,23 +234,23 @@ public void GetDeploymentItemsShouldReturnClassLevelDeploymentItemsOnly() public void GetDeploymentItemsShouldReturnClassAndMethodLevelDeploymentItems() { // Arrange. - KeyValuePair[] deploymentItemAttributes = new[] - { + KeyValuePair[] deploymentItemAttributes = + [ new KeyValuePair( _defaultDeploymentItemPath, - _defaultDeploymentItemOutputDirectory), - }; + _defaultDeploymentItemOutputDirectory) + ]; MethodInfo memberInfo = typeof(DeploymentItemUtilityTests).GetMethod( "GetDeploymentItemsShouldReturnClassAndMethodLevelDeploymentItems"); SetupDeploymentItems(memberInfo, deploymentItemAttributes); - DeploymentItem[] classLevelDeploymentItems = new[] - { + DeploymentItem[] classLevelDeploymentItems = + [ new DeploymentItem( _defaultDeploymentItemPath + "\\temp2", - _defaultDeploymentItemOutputDirectory), - }; + _defaultDeploymentItemOutputDirectory) + ]; // Act. KeyValuePair[] deploymentItems = _deploymentItemUtility.GetDeploymentItems( @@ -275,15 +275,15 @@ public void GetDeploymentItemsShouldReturnClassAndMethodLevelDeploymentItems() public void GetDeploymentItemsShouldReturnClassAndMethodLevelDeploymentItemsWithoutDuplicates() { // Arrange. - KeyValuePair[] deploymentItemAttributes = new[] - { + KeyValuePair[] deploymentItemAttributes = + [ new KeyValuePair( _defaultDeploymentItemPath, _defaultDeploymentItemOutputDirectory), new KeyValuePair( _defaultDeploymentItemPath + "\\temp2", - _defaultDeploymentItemOutputDirectory), - }; + _defaultDeploymentItemOutputDirectory) + ]; MethodInfo memberInfo = typeof(DeploymentItemUtilityTests).GetMethod( "GetDeploymentItemsShouldReturnClassAndMethodLevelDeploymentItems"); @@ -406,12 +406,12 @@ public void HasDeployItemsShouldReturnFalseForNoDeploymentItems() public void HasDeployItemsShouldReturnTrueWhenDeploymentItemsArePresent() { TestCase testCase = new("A.C.M", new System.Uri("executor://testExecutor"), "A"); - KeyValuePair[] kvpArray = new[] - { + KeyValuePair[] kvpArray = + [ new KeyValuePair( _defaultDeploymentItemPath, - _defaultDeploymentItemOutputDirectory), - }; + _defaultDeploymentItemOutputDirectory) + ]; testCase.SetPropertyValue(DeploymentItemsProperty, kvpArray); Verify(DeploymentItemUtility.HasDeploymentItems(testCase)); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13ReflectionUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13ReflectionUtilityTests.cs index badf5a047c..aa9a5fa44c 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13ReflectionUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13ReflectionUtilityTests.cs @@ -23,7 +23,7 @@ public void GetCustomAttributesShouldReturnAllAttributes() Verify(attributes is not null); Verify(attributes.Count == 2); - string[] expectedAttributes = new string[] { "DummyA : base", "DummySingleA : base" }; + string[] expectedAttributes = ["DummyA : base", "DummySingleA : base"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -36,7 +36,7 @@ public void GetCustomAttributesShouldReturnAllAttributesIgnoringBaseInheritance( Verify(attributes is not null); Verify(attributes.Count == 2); - string[] expectedAttributes = new string[] { "DummyA : derived", "DummySingleA : derived" }; + string[] expectedAttributes = ["DummyA : derived", "DummySingleA : derived"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -50,7 +50,7 @@ public void GetCustomAttributesShouldReturnAllAttributesWithBaseInheritance() Verify(attributes.Count == 3); // Notice that the DummySingleA on the base method does not show up since it can only be defined once. - string[] expectedAttributes = new string[] { "DummyA : derived", "DummySingleA : derived", "DummyA : base", }; + string[] expectedAttributes = ["DummyA : derived", "DummySingleA : derived", "DummyA : base"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -63,7 +63,7 @@ public void GetCustomAttributesOnTypeShouldReturnAllAttributes() Verify(attributes is not null); Verify(attributes.Count == 1); - string[] expectedAttributes = new string[] { "DummyA : ba" }; + string[] expectedAttributes = ["DummyA : ba"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -76,7 +76,7 @@ public void GetCustomAttributesOnTypeShouldReturnAllAttributesIgnoringBaseInheri Verify(attributes is not null); Verify(attributes.Count == 1); - string[] expectedAttributes = new string[] { "DummyA : a" }; + string[] expectedAttributes = ["DummyA : a"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -89,7 +89,7 @@ public void GetCustomAttributesOnTypeShouldReturnAllAttributesWithBaseInheritanc Verify(attributes is not null); Verify(attributes.Count == 2); - string[] expectedAttributes = new string[] { "DummyA : a", "DummyA : ba" }; + string[] expectedAttributes = ["DummyA : a", "DummyA : ba"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -102,7 +102,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributes() Verify(attributes is not null); Verify(attributes.Count == 1); - string[] expectedAttributes = new string[] { "DummyA : base" }; + string[] expectedAttributes = ["DummyA : base"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -115,7 +115,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributesIgnoringBaseInhe Verify(attributes is not null); Verify(attributes.Count == 1); - string[] expectedAttributes = new string[] { "DummyA : derived" }; + string[] expectedAttributes = ["DummyA : derived"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -128,7 +128,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributesWithBaseInherita Verify(attributes is not null); Verify(attributes.Count == 2); - string[] expectedAttributes = new string[] { "DummyA : derived", "DummyA : base", }; + string[] expectedAttributes = ["DummyA : derived", "DummyA : base"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -141,7 +141,7 @@ public void GetSpecificCustomAttributesOnTypeShouldReturnAllAttributes() Verify(attributes is not null); Verify(attributes.Count == 1); - string[] expectedAttributes = new string[] { "DummyA : ba" }; + string[] expectedAttributes = ["DummyA : ba"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -154,7 +154,7 @@ public void GetSpecificCustomAttributesOnTypeShouldReturnAllAttributesIgnoringBa Verify(attributes is not null); Verify(attributes.Count == 1); - string[] expectedAttributes = new string[] { "DummyA : a" }; + string[] expectedAttributes = ["DummyA : a"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -167,7 +167,7 @@ public void GetSpecificCustomAttributesOnTypeShouldReturnAllAttributesWithBaseIn Verify(attributes is not null); Verify(attributes.Count == 2); - string[] expectedAttributes = new string[] { "DummyA : a", "DummyA : ba" }; + string[] expectedAttributes = ["DummyA : a", "DummyA : ba"]; Verify(expectedAttributes.SequenceEqual(GetAttributeValuePairs(attributes))); } @@ -176,15 +176,13 @@ internal static List GetAttributeValuePairs(IEnumerable attributes) var attribValuePairs = new List(); foreach (object attrib in attributes) { - if (attrib is DummySingleAAttribute) + if (attrib is DummySingleAAttribute dummySingleAAttribute) { - var a = attrib as DummySingleAAttribute; - attribValuePairs.Add("DummySingleA : " + a.Value); + attribValuePairs.Add("DummySingleA : " + dummySingleAAttribute.Value); } - else if (attrib is DummyAAttribute) + else if (attrib is DummyAAttribute dummyAAttribute) { - var a = attrib as DummyAAttribute; - attribValuePairs.Add("DummyA : " + a.Value); + attribValuePairs.Add("DummyA : " + dummyAAttribute.Value); } } @@ -231,7 +229,7 @@ public DummyAAttribute(string foo) public string Value { get; set; } } - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)] public class DummySingleAAttribute : Attribute { public DummySingleAAttribute(string foo) diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorTests.cs index 6b78bdd708..42ed907228 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.ObjectModel; @@ -50,12 +50,14 @@ protected override void Dispose(bool disposing) public void ConstructorShouldPopulateSettings() { string runSettingsXml = - @" - - True - DummyPath\TestSettings1.testsettings - - "; + """ + + + True + DummyPath\TestSettings1.testsettings + + + """; _testablePlatformServiceProvider.MockSettingsProvider.Setup(sp => sp.Load(It.IsAny())) .Callback((XmlReader actualReader) => @@ -86,19 +88,19 @@ public void GetTypesShouldReturnEmptyArrayWhenNoDeclaredTypes() Mock mockAssembly = new(); // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes).Returns(new List()); + mockAssembly.Setup(a => a.GetTypes()).Returns([]); - Verify(AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings).Count == 0); + Verify(AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings).Length == 0); } public void GetTypesShouldReturnSetOfDefinedTypes() { Mock mockAssembly = new(); - var expectedTypes = new List() { typeof(DummyTestClass).GetTypeInfo(), typeof(DummyTestClass).GetTypeInfo() }; + Type[] expectedTypes = [typeof(DummyTestClass), typeof(DummyTestClass)]; // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes).Returns(expectedTypes); + mockAssembly.Setup(a => a.GetTypes()).Returns(expectedTypes); IReadOnlyList types = AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings); Verify(expectedTypes.SequenceEqual(types)); @@ -109,7 +111,7 @@ public void GetTypesShouldHandleReflectionTypeLoadException() Mock mockAssembly = new(); // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes).Throws(new ReflectionTypeLoadException(null, null)); + mockAssembly.Setup(a => a.GetTypes()).Throws(new ReflectionTypeLoadException(null, null)); AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings); } @@ -120,7 +122,7 @@ public void GetTypesShouldReturnReflectionTypeLoadExceptionTypesOnException() var reflectedTypes = new Type[] { typeof(DummyTestClass) }; // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes).Throws(new ReflectionTypeLoadException(reflectedTypes, null)); + mockAssembly.Setup(a => a.GetTypes()).Throws(new ReflectionTypeLoadException(reflectedTypes, null)); IReadOnlyList types = AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings); @@ -134,7 +136,7 @@ public void GetTypesShouldLogWarningsWhenReflectionFailsWithLoaderExceptions() var exceptions = new Exception[] { new("DummyLoaderException") }; // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes).Throws(new ReflectionTypeLoadException(null, exceptions)); + mockAssembly.Setup(a => a.GetTypes()).Throws(new ReflectionTypeLoadException(null, exceptions)); IReadOnlyList types = AssemblyEnumerator.GetTypes(mockAssembly.Object, "DummyAssembly", _warnings); @@ -218,7 +220,7 @@ public void EnumerateAssemblyShouldReturnEmptyListWhenNoDeclaredTypes() Mock mockAssembly = CreateMockTestableAssembly(); // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes).Returns(new List()); + mockAssembly.Setup(a => a.GetTypes()).Returns([]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); @@ -231,8 +233,8 @@ public void EnumerateAssemblyShouldReturnEmptyListWhenNoTestElementsInAType() var testableAssemblyEnumerator = new TestableAssemblyEnumerator(); // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes) - .Returns(new List() { typeof(DummyTestClass).GetTypeInfo() }); + mockAssembly.Setup(a => a.GetTypes()) + .Returns([typeof(DummyTestClass)]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out _warnings)) @@ -248,8 +250,8 @@ public void EnumerateAssemblyShouldReturnTestElementsForAType() var unitTestElement = new UnitTestElement(new TestMethod("DummyMethod", "DummyClass", "DummyAssembly", false)); // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes) - .Returns(new List() { typeof(DummyTestClass).GetTypeInfo() }); + mockAssembly.Setup(a => a.GetTypes()) + .Returns([typeof(DummyTestClass)]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out _warnings)) @@ -268,8 +270,8 @@ public void EnumerateAssemblyShouldReturnMoreThanOneTestElementForAType() var expectedTestElements = new Collection { unitTestElement, unitTestElement }; // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes) - .Returns(new List() { typeof(DummyTestClass).GetTypeInfo() }); + mockAssembly.Setup(a => a.GetTypes()) + .Returns([typeof(DummyTestClass)]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out _warnings)) @@ -288,8 +290,8 @@ public void EnumerateAssemblyShouldReturnMoreThanOneTestElementForMoreThanOneTyp var expectedTestElements = new Collection { unitTestElement, unitTestElement }; // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes) - .Returns(new List() { typeof(DummyTestClass).GetTypeInfo(), typeof(DummyTestClass).GetTypeInfo() }); + mockAssembly.Setup(a => a.GetTypes()) + .Returns([typeof(DummyTestClass), typeof(DummyTestClass)]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out _warnings)) @@ -306,11 +308,11 @@ public void EnumerateAssemblyShouldNotLogWarningsIfNonePresent() { Mock mockAssembly = CreateMockTestableAssembly(); var testableAssemblyEnumerator = new TestableAssemblyEnumerator(); - ICollection warningsFromTypeEnumerator = null; + ICollection warningsFromTypeEnumerator = []; // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes) - .Returns(new List() { typeof(InternalTestClass).GetTypeInfo() }); + mockAssembly.Setup(a => a.GetTypes()) + .Returns([typeof(InternalTestClass)]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out warningsFromTypeEnumerator)); @@ -329,8 +331,8 @@ public void EnumerateAssemblyShouldLogWarningsIfPresent() }; // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes) - .Returns(new List() { typeof(InternalTestClass).GetTypeInfo() }); + mockAssembly.Setup(a => a.GetTypes()) + .Returns([typeof(InternalTestClass)]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out warningsFromTypeEnumerator)); @@ -347,8 +349,8 @@ public void EnumerateAssemblyShouldHandleExceptionsWhileEnumeratingAType() var exception = new Exception("TypeEnumerationException"); // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes) - .Returns(new List() { typeof(InternalTestClass).GetTypeInfo() }); + mockAssembly.Setup(a => a.GetTypes()) + .Returns([typeof(InternalTestClass)]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out _warnings)).Throws(exception); @@ -396,9 +398,7 @@ private static Mock CreateMockTestableAssembly() #region Testable Implementations -public class TestableAssembly : Assembly -{ -} +public class TestableAssembly : Assembly; internal sealed class TestableAssemblyEnumerator : AssemblyEnumerator { @@ -413,13 +413,14 @@ internal TestableAssemblyEnumerator() reflectHelper.Object, typeValidator.Object, testMethodValidator.Object, + TestDataSourceDiscoveryOption.DuringExecution, TestIdGenerationStrategy.FullyQualified); } internal Mock MockTypeEnumerator { get; set; } internal override TypeEnumerator GetTypeEnumerator(Type type, string assemblyFileName, bool discoverInternals, - TestIdGenerationStrategy testIdGenerationStrategy) + TestDataSourceDiscoveryOption discoveryOption, TestIdGenerationStrategy testIdGenerationStrategy) => MockTypeEnumerator.Object; } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TestMethodValidatorTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TestMethodValidatorTests.cs index dafafa633f..e7c3a59f05 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TestMethodValidatorTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TestMethodValidatorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Diagnostics.CodeAnalysis; @@ -39,7 +39,7 @@ public TestMethodValidatorTests() public void IsValidTestMethodShouldReturnFalseForMethodsWithoutATestMethodAttributeOrItsDerivedAttributes() { _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(It.IsAny(), false)).Returns(false); + rh => rh.IsDerivedAttributeDefined(It.IsAny(), false)).Returns(false); Verify(!_testMethodValidator.IsValidTestMethod(_mockMethodInfo.Object, _type, _warnings)); } @@ -56,6 +56,7 @@ public void IsValidTestMethodShouldReturnFalseForGenericTestMethodDefinitions() public void IsValidTestMethodShouldReportWarningsForGenericTestMethodDefinitions() { SetupTestMethod(); + _mockMethodInfo.Setup(mi => mi.IsGenericMethodDefinition).Returns(true); _mockMethodInfo.Setup(mi => mi.DeclaringType.FullName).Returns("DummyTestClass"); _mockMethodInfo.Setup(mi => mi.Name).Returns("DummyTestMethod"); @@ -183,7 +184,7 @@ public void WhenDiscoveryOfInternalsIsEnabledIsValidTestMethodShouldReturnFalseF #endregion private void SetupTestMethod() => _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(It.IsAny(), false)).Returns(true); + rh => rh.IsDerivedAttributeDefined(It.IsAny(), false)).Returns(true); } #region Dummy types diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.cs index c8761125ba..2c709ce884 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Reflection; @@ -98,6 +98,23 @@ public void GetTestsShouldReturnBaseTestMethodsInSameAssembly() public void GetTestsShouldReturnBaseTestMethodsFromAnotherAssemblyByDefault() { + string runSettingsXml = + """ + + + true + false + true + + + """; + + var mockRunContext = new Mock(); + var mockRunSettings = new Mock(); + mockRunContext.Setup(dc => dc.RunSettings).Returns(mockRunSettings.Object); + mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); + + MSTestSettings.PopulateSettings(mockRunContext.Object); SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: false); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyDerivedTestClass), Assembly.GetExecutingAssembly().FullName); @@ -113,13 +130,15 @@ public void GetTestsShouldReturnBaseTestMethodsFromAnotherAssemblyByDefault() public void GetTestsShouldReturnBaseTestMethodsFromAnotherAssemblyByConfiguration() { string runSettingsXml = - @" - - true - false - true + """ + + + true + false + true - "; + + """; var mockRunContext = new Mock(); var mockRunSettings = new Mock(); @@ -142,13 +161,15 @@ public void GetTestsShouldReturnBaseTestMethodsFromAnotherAssemblyByConfiguratio public void GetTestsShouldNotReturnBaseTestMethodsFromAnotherAssemblyByConfiguration() { string runSettingsXml = - @" - - true - false - false + """ + + + true + false + false - "; + + """; var mockRunContext = new Mock(); var mockRunSettings = new Mock(); @@ -277,9 +298,9 @@ public void GetTestFromMethodShouldSetIgnoredPropertyToFalseIfNotSetOnTestClassA // Setup mocks _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(typeof(DummyTestClass), false)).Returns(false); + rh => rh.IsNonDerivedAttributeDefined(typeof(DummyTestClass), false)).Returns(false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(methodInfo, false)).Returns(false); + rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)).Returns(false); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -292,10 +313,10 @@ public void GetTestFromMethodShouldSetTestCategory() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - string[] testCategories = new string[] { "foo", "bar" }; + string[] testCategories = ["foo", "bar"]; // Setup mocks - _mockReflectHelper.Setup(rh => rh.GetCategories(methodInfo, typeof(DummyTestClass))).Returns(testCategories); + _mockReflectHelper.Setup(rh => rh.GetTestCategories(methodInfo, typeof(DummyTestClass))).Returns(testCategories); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -310,7 +331,7 @@ public void GetTestFromMethodShouldSetDoNotParallelize() MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); // Setup mocks - _mockReflectHelper.Setup(rh => rh.GetCustomAttributes(It.IsAny())).Returns([new DoNotParallelizeAttribute()]); + _mockReflectHelper.Setup(rh => rh.IsDerivedAttributeDefined(It.IsAny(), true)).Returns(true); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -393,7 +414,7 @@ public void GetTestFromMethodShouldSetDescription() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - _mockReflectHelper.Setup(rh => rh.GetCustomAttribute(methodInfo)).Returns(new DescriptionAttribute("Dummy description")); + _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(methodInfo, true)).Returns(new DescriptionAttribute("Dummy description")); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -405,7 +426,7 @@ public void GetTestFromMethodShouldSetWorkItemIds() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - _mockReflectHelper.Setup(rh => rh.GetCustomAttributes(methodInfo)).Returns([new(123), new(345)]); + _mockReflectHelper.Setup(rh => rh.GetDerivedAttributes(methodInfo, true)).Returns([new(123), new(345)]); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -417,7 +438,7 @@ public void GetTestFromMethodShouldSetWorkItemIdsToNullIfNotAny() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - _mockReflectHelper.Setup(rh => rh.GetCustomAttributes(methodInfo)).Returns(Array.Empty()); + _mockReflectHelper.Setup(rh => rh.GetDerivedAttributes(methodInfo, true)).Returns([]); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -429,7 +450,7 @@ public void GetTestFromMethodShouldSetCssIteration() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - _mockReflectHelper.Setup(rh => rh.GetCustomAttribute(methodInfo)).Returns(new CssIterationAttribute("234")); + _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(methodInfo, true)).Returns(new CssIterationAttribute("234")); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -441,7 +462,7 @@ public void GetTestFromMethodShouldSetCssProjectStructure() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - _mockReflectHelper.Setup(rh => rh.GetCustomAttribute(methodInfo)).Returns(new CssProjectStructureAttribute("ProjectStructure123")); + _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(methodInfo, true)).Returns(new CssProjectStructureAttribute("ProjectStructure123")); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -470,7 +491,7 @@ public void GetTestFromMethodShouldSetDeploymentItems() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - KeyValuePair[] deploymentItems = new[] { new KeyValuePair("C:\\temp", string.Empty) }; + KeyValuePair[] deploymentItems = [new KeyValuePair("C:\\temp", string.Empty)]; // Setup mocks _testablePlatformServiceProvider.MockTestDeployment.Setup( @@ -509,7 +530,7 @@ public void GetTestFromMethodShouldSetDisplayNameToTestMethodNameIfDisplayNameIs // Setup mocks to behave like we have [TestMethod] attribute on the method _mockReflectHelper.Setup( - rh => rh.GetCustomAttribute(It.IsAny())).Returns(new TestMethodAttribute()); + rh => rh.GetFirstDerivedAttributeOrDefault(It.IsAny(), false)).Returns(new TestMethodAttribute()); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -525,7 +546,7 @@ public void GetTestFromMethodShouldSetDisplayNameFromTestMethodAttribute() // Setup mocks to behave like we have [TestMethod("Test method display name.")] attribute on the method _mockReflectHelper.Setup( - rh => rh.GetCustomAttribute(methodInfo)).Returns(new TestMethodAttribute("Test method display name.")); + rh => rh.GetFirstDerivedAttributeOrDefault(methodInfo, true)).Returns(new TestMethodAttribute("Test method display name.")); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -541,7 +562,7 @@ public void GetTestFromMethodShouldSetDisplayNameFromDataTestMethodAttribute() // Setup mocks to behave like we have [DataTestMethod("Test method display name.")] attribute on the method _mockReflectHelper.Setup( - rh => rh.GetCustomAttribute(methodInfo)).Returns(new DataTestMethodAttribute("Test method display name.")); + rh => rh.GetFirstDerivedAttributeOrDefault(methodInfo, true)).Returns(new DataTestMethodAttribute("Test method display name.")); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -564,6 +585,7 @@ private void SetupTestClassAndTestMethods(bool isValidTestClass, bool isValidTes } private TypeEnumerator GetTypeEnumeratorInstance(Type type, string assemblyName, + TestDataSourceDiscoveryOption discoveryOption = TestDataSourceDiscoveryOption.DuringExecution, TestIdGenerationStrategy idGenerationStrategy = TestIdGenerationStrategy.FullyQualified) => new( type, @@ -571,6 +593,7 @@ private TypeEnumerator GetTypeEnumeratorInstance(Type type, string assemblyName, _mockReflectHelper.Object, _mockTypeValidator.Object, _mockTestMethodValidator.Object, + discoveryOption, idGenerationStrategy); #endregion diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeValidatorTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeValidatorTests.cs index 8b32e05d13..f9b1a109ca 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeValidatorTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeValidatorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Globalization; @@ -44,15 +44,15 @@ public TypeValidatorTests() public void IsValidTestClassShouldReturnFalseForClassesNotHavingTestClassAttributeOrDerivedAttributeTypes() { - _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(It.IsAny(), false)).Returns(false); + _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(It.IsAny(), false)).Returns(false); Verify(!_typeValidator.IsValidTestClass(typeof(TypeValidatorTests), _warnings)); } public void IsValidTestClassShouldReturnTrueForClassesMarkedByAnAttributeDerivedFromTestClass() { - _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(It.IsAny(), false)).Returns(false); + _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(It.IsAny(), false)).Returns(false); _mockReflectHelper.Setup( - rh => rh.HasAttributeDerivedFrom(It.IsAny(), false)).Returns(true); + rh => rh.IsDerivedAttributeDefined(It.IsAny(), false)).Returns(true); Verify(_typeValidator.IsValidTestClass(typeof(TypeValidatorTests), _warnings)); } @@ -264,14 +264,14 @@ public void TypeHasValidAccessibilityShouldReturnTrueForAllPublicTypesIncludingN { Type[] allTypes = GetAllTestTypes(); - string[] expectedDiscoveredTypes = new[] - { + string[] expectedDiscoveredTypes = + [ nameof(PublicClass2), nameof(PublicClass3), nameof(PublicClass2.PublicNestedClassInPublicClass), nameof(PublicClass3.PublicClassNestedInPublicClass), - nameof(PublicClass3.PublicClassNestedInPublicClass.PublicClassNestedInPublicClassNestedInPublicClass), - }; + nameof(PublicClass3.PublicClassNestedInPublicClass.PublicClassNestedInPublicClassNestedInPublicClass) + ]; bool discoverInternal = false; string[] actualDiscoveredTypes = allTypes @@ -287,8 +287,8 @@ public void TypeHasValidAccessibilityShouldReturnFalseForAllTypesThatAreNotPubli { Type[] allTypes = GetAllTestTypes(); - string[] expectedNonDiscoveredTypes = new[] - { + string[] expectedNonDiscoveredTypes = + [ nameof(InternalClass), nameof(InternalClass.PublicClassNestedInInternalClass), nameof(InternalClass.InternalClassNestedInInternalClass), @@ -313,8 +313,8 @@ public void TypeHasValidAccessibilityShouldReturnFalseForAllTypesThatAreNotPubli nameof(PrivateClassNames.PublicClassNestedInPrivateClassNestedInPublicClass), nameof(PrivateClassNames.PublicClassNestedInPrivateClassNestedInInternalClass), nameof(PrivateClassNames.PrivateClassNestedInPublicClass), // from PublicClass3 - nameof(PrivateClassNames.PrivateClassNestedInInternalClass), - }; + nameof(PrivateClassNames.PrivateClassNestedInInternalClass) + ]; bool discoverInternal = false; string[] actualDiscoveredTypes = allTypes @@ -330,8 +330,8 @@ public void TypeHasValidAccessibilityShouldReturnTrueForAllPublicAndInternalType { Type[] allTypes = GetAllTestTypes(); - string[] expectedDiscoveredTypes = new[] - { + string[] expectedDiscoveredTypes = + [ nameof(PublicClass2), nameof(PublicClass3), nameof(InternalClass), @@ -348,8 +348,8 @@ public void TypeHasValidAccessibilityShouldReturnTrueForAllPublicAndInternalType nameof(PublicClass3.InternalClassNestedInPublicClass.InternalClassNestedInInternalClassNestedInPublicClass), nameof(InternalClass2.InternalClassNestedInInternalClass), nameof(InternalClass2.InternalClassNestedInInternalClass.PublicClassNestedInInternalClassNestedInInternalClass), - nameof(InternalClass2.InternalClassNestedInInternalClass.InternalClassNestedInInternalClassNestedInInternalClass), - }; + nameof(InternalClass2.InternalClassNestedInInternalClass.InternalClassNestedInInternalClassNestedInInternalClass) + ]; bool discoverInternal = true; string[] actualDiscoveredTypes = allTypes @@ -365,8 +365,8 @@ public void TypeHasValidAccessibilityShouldReturnFalseForAllTypesThatAreNotPubli { Type[] allTypes = GetAllTestTypes(); - string[] expectedNonDiscoveredTypes = new[] - { + string[] expectedNonDiscoveredTypes = + [ nameof(PrivateClassNames.ProtectedInteralNestedClassInPublicClass), nameof(PrivateClassNames.ProtectedNestedClassInPublicClass), nameof(PrivateClassNames.PrivateProtectedNestedClassInPublicClass), @@ -378,8 +378,8 @@ public void TypeHasValidAccessibilityShouldReturnFalseForAllTypesThatAreNotPubli nameof(PrivateClassNames.PublicClassNestedInPrivateClassNestedInPublicClass), nameof(PrivateClassNames.PublicClassNestedInPrivateClassNestedInInternalClass), nameof(PrivateClassNames.PrivateClassNestedInPublicClass), // from PublicClass3 - nameof(PrivateClassNames.PrivateClassNestedInInternalClass), - }; + nameof(PrivateClassNames.PrivateClassNestedInInternalClass) + ]; bool discoverInternal = true; string[] actualDiscoveredTypes = allTypes @@ -393,7 +393,7 @@ public void TypeHasValidAccessibilityShouldReturnFalseForAllTypesThatAreNotPubli private static Type[] GetAllTestTypes() { - Type[] types = new[] { typeof(PublicClass2), typeof(PublicClass3), typeof(InternalClass), typeof(InternalClass2) }; + Type[] types = [typeof(PublicClass2), typeof(PublicClass3), typeof(InternalClass), typeof(InternalClass2)]; Type[] nestedTypes = types.SelectMany(t => t.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic)).ToArray(); Type[] nestedNestedTypes = nestedTypes.SelectMany(t => t.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic)).ToArray(); Type[] allTypes = new[] { types, nestedTypes, nestedNestedTypes }.SelectMany(t => t).ToArray(); @@ -403,7 +403,7 @@ private static Type[] GetAllTestTypes() #region private methods - private void SetupTestClass() => _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(It.IsAny(), false)).Returns(true); + private void SetupTestClass() => _mockReflectHelper.Setup(rh => rh.IsDerivedAttributeDefined(It.IsAny(), false)).Returns(true); #endregion } @@ -416,13 +416,9 @@ public interface IDummyInterface { } -public abstract class AbstractGenericClass -{ -} +public abstract class AbstractGenericClass; -public class GenericClass -{ -} +public class GenericClass; public class ClassWithTestContextGetterOnly { @@ -473,140 +469,88 @@ public class GenericClassWithTestContext public UTFExtension.TestContext TestContext { get; set; } } -public class PublicTestClass -{ -} +public class PublicTestClass; -public abstract class AbstractTestClass -{ -} +public abstract class AbstractTestClass; public class OuterClass { - public class NestedPublicClass - { - } + public class NestedPublicClass; - internal class NestedInternalClass - { - } + internal class NestedInternalClass; private class NestedPrivateClass { - public class InaccessiblePublicClass - { - } + public class InaccessiblePublicClass; } } public class PublicClass2 { - public class PublicNestedClassInPublicClass - { - } + public class PublicNestedClassInPublicClass; - internal class InternalNestedClassInPublicClass - { - } + internal class InternalNestedClassInPublicClass; - protected internal class ProtectedInteralNestedClassInPublicClass - { - } + protected internal class ProtectedInteralNestedClassInPublicClass; - protected class ProtectedNestedClassInPublicClass - { - } + protected class ProtectedNestedClassInPublicClass; - private protected class PrivateProtectedNestedClassInPublicClass - { - } + private protected class PrivateProtectedNestedClassInPublicClass; - private sealed class PrivateClassNestedInPublicClass - { - } + private sealed class PrivateClassNestedInPublicClass; } public class PublicClass3 { public class PublicClassNestedInPublicClass { - public class PublicClassNestedInPublicClassNestedInPublicClass - { - } + public class PublicClassNestedInPublicClassNestedInPublicClass; - internal class InternalClassNestedInPublicClassNestedInPublicClass - { - } + internal class InternalClassNestedInPublicClassNestedInPublicClass; } internal class InternalClassNestedInPublicClass { - public class PublicClassNestedInInternalClassNestedInPublicClass - { - } + public class PublicClassNestedInInternalClassNestedInPublicClass; - internal class InternalClassNestedInInternalClassNestedInPublicClass - { - } + internal class InternalClassNestedInInternalClassNestedInPublicClass; } private sealed class PrivateClassNestedInPublicClass { - public sealed class PublicClassNestedInPrivateClassNestedInPublicClass - { - } + public sealed class PublicClassNestedInPrivateClassNestedInPublicClass; } } -internal class InternalTestClass -{ -} +internal class InternalTestClass; internal class InternalClass { - public class PublicClassNestedInInternalClass - { - } + public class PublicClassNestedInInternalClass; - internal class InternalClassNestedInInternalClass - { - } + internal class InternalClassNestedInInternalClass; - protected internal class ProtectedInteralClassNestedInInternalClass - { - } + protected internal class ProtectedInteralClassNestedInInternalClass; - protected class ProtectedClassNestedInInternalClass - { - } + protected class ProtectedClassNestedInInternalClass; - private protected class PrivateProtectedClassNestedInInternalClass - { - } + private protected class PrivateProtectedClassNestedInInternalClass; - private sealed class PrivateClassNestedInInternalClass - { - } + private sealed class PrivateClassNestedInInternalClass; } internal class InternalClass2 { internal class InternalClassNestedInInternalClass { - public class PublicClassNestedInInternalClassNestedInInternalClass - { - } + public class PublicClassNestedInInternalClassNestedInInternalClass; - internal class InternalClassNestedInInternalClassNestedInInternalClass - { - } + internal class InternalClassNestedInInternalClassNestedInInternalClass; } private sealed class PrivateClassNestedInInternalClass { - public sealed class PublicClassNestedInPrivateClassNestedInInternalClass - { - } + public sealed class PublicClassNestedInPrivateClassNestedInInternalClass; } } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/UnitTestDiscovererTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/UnitTestDiscovererTests.cs index 248101b5e3..a5d8703698 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/UnitTestDiscovererTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/UnitTestDiscovererTests.cs @@ -62,7 +62,7 @@ protected override void Dispose(bool disposing) public void DiscoverTestsShouldDiscoverForAllSources() { - string[] sources = new string[] { "DummyAssembly1.dll", "DummyAssembly2.dll" }; + string[] sources = ["DummyAssembly1.dll", "DummyAssembly2.dll"]; // Setup mocks. foreach (string source in sources) @@ -102,12 +102,14 @@ public void DiscoverTestsInSourceShouldSendBackHandleTreatAsError() .Returns(false); string settingsXml = - @" - - - True - - "; + """ + + + + True + + + """; _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(settingsXml); @@ -176,13 +178,15 @@ public void SendTestCasesShouldSendAllTestCaseData() public void SendTestCasesShouldSendTestCasesWithoutNavigationDataWhenCollectSourceInformationIsFalse() { string settingsXml = - @" - - - .\TestResults - false - - "; + """ + + + + .\TestResults + false + + + """; // Setup mocks. _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.CreateNavigationSession(Source)) @@ -239,11 +243,6 @@ public void SendTestCasesShouldUseNavigationSessionForDeclaredAssemblyName() .Returns((object)null); _test.TestMethod.DeclaringAssemblyName = "DummyAssembly2.dll"; - ////var test = new UnitTestElement( - //// new TestMethod("M", "C", "A", false) - //// { - //// DeclaringAssemblyName = "DummyAssembly2.dll" - //// }); SetupNavigation(Source, _test, _test.TestMethod.DeclaringClassFullName, _test.TestMethod.Name); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TcmTestPropertiesProviderTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TcmTestPropertiesProviderTests.cs index 9d54b15c5d..b3a1522bc9 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TcmTestPropertiesProviderTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TcmTestPropertiesProviderTests.cs @@ -40,13 +40,24 @@ public void GetTcmPropertiesShouldReturnEmptyDictionaryIfTestCaseIsNull() public void GetTcmPropertiesShouldReturnEmptyDictionaryIfTestCaseIdIsZero() { var testCase = new TestCase("PassingTestFomTestCase", new Uri("http://sampleUri/"), "unittestproject1.dll"); - object[] propertiesValue = new object[] - { - 32, 534, 5, "sample build directory", "sample build flavor", - "132456", "sample build platform", "http://sampleBuildUri/", - "http://samplecollectionuri/", "sample team project", false, - 0, 54, "sample configuration name", 345, - }; + object[] propertiesValue = + [ + 32, + 534, + 5, + "sample build directory", + "sample build flavor", + "132456", + "sample build platform", + "http://sampleBuildUri/", + "http://samplecollectionuri/", + "sample team project", + false, + 0, + 54, + "sample configuration name", + 345 + ]; SetTestCaseProperties(testCase, propertiesValue); IDictionary tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase); @@ -56,13 +67,24 @@ public void GetTcmPropertiesShouldReturnEmptyDictionaryIfTestCaseIdIsZero() public void GetTcmPropertiesShouldGetAllPropertiesFromTestCase() { var testCase = new TestCase("PassingTestFomTestCase", new Uri("http://sampleUri/"), "unittestproject1.dll"); - object[] propertiesValue = new object[] - { - 32, 534, 5, "sample build directory", "sample build flavor", - "132456", "sample build platform", "http://sampleBuildUri/", - "http://samplecollectionuri/", "sample team project", false, - 1401, 54, "sample configuration name", 345, - }; + object[] propertiesValue = + [ + 32, + 534, + 5, + "sample build directory", + "sample build flavor", + "132456", + "sample build platform", + "http://sampleBuildUri/", + "http://samplecollectionuri/", + "sample team project", + false, + 1401, + 54, + "sample configuration name", + 345 + ]; SetTestCaseProperties(testCase, propertiesValue); IDictionary tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase); @@ -74,26 +96,48 @@ public void GetTcmPropertiesShouldCopyMultiplePropertiesCorrectlyFromTestCase() { // Verify 1st call. var testCase1 = new TestCase("PassingTestFomTestCase", new Uri("http://sampleUri/"), "unittestproject1.dll"); - object[] propertiesValue1 = new object[] - { - 32, 534, 5, "sample build directory", "sample build flavor", - "132456", "sample build platform", "http://sampleBuildUri/", - "http://samplecollectionuri/", "sample team project", false, - 1401, 54, "sample configuration name", 345, - }; + object[] propertiesValue1 = + [ + 32, + 534, + 5, + "sample build directory", + "sample build flavor", + "132456", + "sample build platform", + "http://sampleBuildUri/", + "http://samplecollectionuri/", + "sample team project", + false, + 1401, + 54, + "sample configuration name", + 345 + ]; SetTestCaseProperties(testCase1, propertiesValue1); IDictionary tcmProperties1 = TcmTestPropertiesProvider.GetTcmProperties(testCase1); VerifyTcmProperties(tcmProperties1, testCase1); // Verify 2nd call. var testCase2 = new TestCase("PassingTestFomTestCase2", new Uri("http://sampleUri2/"), "unittestproject2.dll"); - object[] propertiesValue2 = new object[] - { - 33, 535, 6, "sample build directory 2", "sample build flavor 2", - "132457", "sample build platform 2", "http://sampleBuildUri2/", - "http://samplecollectionuri2/", "sample team project", true, - 1403, 55, "sample configuration name 2", 346, - }; + object[] propertiesValue2 = + [ + 33, + 535, + 6, + "sample build directory 2", + "sample build flavor 2", + "132457", + "sample build platform 2", + "http://sampleBuildUri2/", + "http://samplecollectionuri2/", + "sample team project", + true, + 1403, + 55, + "sample configuration name 2", + 346 + ]; SetTestCaseProperties(testCase2, propertiesValue2); IDictionary tcmProperties2 = TcmTestPropertiesProvider.GetTcmProperties(testCase2); VerifyTcmProperties(tcmProperties2, testCase2); @@ -103,39 +147,72 @@ public void GetTcmPropertiesShouldHandleDuplicateTestsProperlyFromTestCase() { // Verify 1st call. var testCase1 = new TestCase("PassingTestFomTestCase", new Uri("http://sampleUri/"), "unittestproject1.dll"); - object[] propertiesValue1 = new object[] - { - 32, 534, 5, "sample build directory", "sample build flavor", - "132456", "sample build platform", "http://sampleBuildUri/", - "http://samplecollectionuri/", "sample team project", false, - 1401, 54, "sample configuration name", 345, - }; + object[] propertiesValue1 = + [ + 32, + 534, + 5, + "sample build directory", + "sample build flavor", + "132456", + "sample build platform", + "http://sampleBuildUri/", + "http://samplecollectionuri/", + "sample team project", + false, + 1401, + 54, + "sample configuration name", + 345 + ]; SetTestCaseProperties(testCase1, propertiesValue1); IDictionary tcmProperties1 = TcmTestPropertiesProvider.GetTcmProperties(testCase1); VerifyTcmProperties(tcmProperties1, testCase1); // Verify 2nd call. var testCase2 = new TestCase("PassingTestFomTestCase", new Uri("http://sampleUri/"), "unittestproject1.dll"); - object[] propertiesValue2 = new object[] - { - 33, 535, 6, "sample build directory 2", "sample build flavor 2", - "132457", "sample build platform 2", "http://sampleBuildUri2/", - "http://samplecollectionuri2/", "sample team project", true, - 1403, 55, "sample configuration name 2", 346, - }; + object[] propertiesValue2 = + [ + 33, + 535, + 6, + "sample build directory 2", + "sample build flavor 2", + "132457", + "sample build platform 2", + "http://sampleBuildUri2/", + "http://samplecollectionuri2/", + "sample team project", + true, + 1403, + 55, + "sample configuration name 2", + 346 + ]; SetTestCaseProperties(testCase2, propertiesValue2); IDictionary tcmProperties2 = TcmTestPropertiesProvider.GetTcmProperties(testCase2); VerifyTcmProperties(tcmProperties2, testCase2); // Verify 3rd call. var testCase3 = new TestCase("PassingTestFomTestCase2", new Uri("http://sampleUri/"), "unittestproject2.dll"); - object[] propertiesValue3 = new object[] - { - 34, 536, 7, "sample build directory 3", "sample build flavor 3", - "132458", "sample build platform 3", "http://sampleBuildUri3/", - "http://samplecollectionuri3/", "sample team project2", true, - 1404, 55, "sample configuration name 3", 347, - }; + object[] propertiesValue3 = + [ + 34, + 536, + 7, + "sample build directory 3", + "sample build flavor 3", + "132458", + "sample build platform 3", + "http://sampleBuildUri3/", + "http://samplecollectionuri3/", + "sample team project2", + true, + 1404, + 55, + "sample configuration name 3", + 347 + ]; SetTestCaseProperties(testCase3, propertiesValue3); IDictionary tcmProperties3 = TcmTestPropertiesProvider.GetTcmProperties(testCase3); VerifyTcmProperties(tcmProperties3, testCase3); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblyInfoTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblyInfoTests.cs index 89d0387bf8..6504fb4576 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblyInfoTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblyInfoTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Reflection; @@ -303,11 +303,10 @@ public void RunAssemblyCleanupShouldReturnExceptionDetailsOfNonAssertExceptions( public void RunAssemblyCleanupShouldThrowTheInnerMostExceptionWhenThereAreMultipleNestedTypeInitializationExceptions() { - DummyTestClass.AssemblyCleanupMethodBody = () => - // This helper calls inner helper, and the inner helper ctor throws. - // We want to see the real exception on screen, and not TypeInitializationException - // which has no info about what failed. - FailingStaticHelper.DoWork(); + // This helper calls inner helper, and the inner helper ctor throws. + // We want to see the real exception on screen, and not TypeInitializationException + // which has no info about what failed. + DummyTestClass.AssemblyCleanupMethodBody = FailingStaticHelper.DoWork; _testAssemblyInfo.AssemblyCleanupMethod = typeof(DummyTestClass).GetMethod("AssemblyCleanupMethod"); string actualErrorMessage = _testAssemblyInfo.RunAssemblyCleanup(); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblySettingsProviderTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblySettingsProviderTests.cs index a7070c85de..9abcca60a1 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblySettingsProviderTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblySettingsProviderTests.cs @@ -61,7 +61,7 @@ public void GetSettingsShouldSetParallelWorkers() _testablePlatformServiceProvider .MockReflectionOperations .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(UTF.ParallelizeAttribute))) - .Returns(new[] { new UTF.ParallelizeAttribute { Workers = 10 } }); + .Returns([new UTF.ParallelizeAttribute { Workers = 10 }]); // Act. MSTest.TestAdapter.ObjectModel.TestAssemblySettings settings = TestAssemblySettingsProvider.GetSettings("Foo"); @@ -80,7 +80,7 @@ public void GetSettingsShouldSetParallelWorkersToProcessorCountIfZero() _testablePlatformServiceProvider .MockReflectionOperations .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(UTF.ParallelizeAttribute))) - .Returns(new[] { new UTF.ParallelizeAttribute { Workers = 0 } }); + .Returns([new UTF.ParallelizeAttribute { Workers = 0 }]); // Act. MSTest.TestAdapter.ObjectModel.TestAssemblySettings settings = TestAssemblySettingsProvider.GetSettings("Foo"); @@ -114,7 +114,7 @@ public void GetSettingsShouldSetParallelScope() _testablePlatformServiceProvider .MockReflectionOperations .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(UTF.ParallelizeAttribute))) - .Returns(new[] { new UTF.ParallelizeAttribute { Scope = UTF.ExecutionScope.MethodLevel } }); + .Returns([new UTF.ParallelizeAttribute { Scope = UTF.ExecutionScope.MethodLevel }]); // Act. MSTest.TestAdapter.ObjectModel.TestAssemblySettings settings = TestAssemblySettingsProvider.GetSettings("Foo"); @@ -148,7 +148,7 @@ public void GetSettingsShouldSetCanParallelizeAssemblyToFalseIfDoNotParallelizeI _testablePlatformServiceProvider .MockReflectionOperations .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(UTF.DoNotParallelizeAttribute))) - .Returns(new[] { new UTF.DoNotParallelizeAttribute() }); + .Returns([new UTF.DoNotParallelizeAttribute()]); // Act. MSTest.TestAdapter.ObjectModel.TestAssemblySettings settings = TestAssemblySettingsProvider.GetSettings("Foo"); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestClassInfoTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestClassInfoTests.cs index 34cc8dcd95..454a2a9894 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestClassInfoTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestClassInfoTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Reflection; @@ -98,8 +98,8 @@ void Action() public void TestClassInfoClassCleanupMethodShouldNotInvokeWhenNoTestClassInitializedIsCalled() { - int classcleanupCallCount = 0; - DummyTestClass.ClassCleanupMethodBody = () => classcleanupCallCount++; + int classCleanupCallCount = 0; + DummyTestClass.ClassCleanupMethodBody = () => classCleanupCallCount++; _testClassInfo.ClassCleanupMethod = typeof(DummyTestClass).GetMethod("ClassCleanupMethod"); _testClassInfo.ClassInitializeMethod = typeof(DummyTestClass).GetMethod("ClassInitializeMethod"); @@ -107,13 +107,13 @@ public void TestClassInfoClassCleanupMethodShouldNotInvokeWhenNoTestClassInitial string ret = _testClassInfo.RunClassCleanup(); // call cleanup without calling init Verify(ret is null); - Verify(classcleanupCallCount == 0); + Verify(classCleanupCallCount == 0); } public void TestClassInfoClassCleanupMethodShouldNotInvokeBaseClassCleanupMethodsWhenNoTestClassInitializedIsCalled() { - int classcleanupCallCount = 0; - DummyBaseTestClass.ClassCleanupMethodBody = () => classcleanupCallCount++; + int classCleanupCallCount = 0; + DummyBaseTestClass.ClassCleanupMethodBody = () => classCleanupCallCount++; _testClassInfo.BaseClassInitAndCleanupMethods.Enqueue( new Tuple( @@ -124,7 +124,7 @@ public void TestClassInfoClassCleanupMethodShouldNotInvokeBaseClassCleanupMethod string ret = _testClassInfo.RunClassCleanup(); // call cleanup without calling init Verify(ret is null); - Verify(classcleanupCallCount == 0); + Verify(classCleanupCallCount == 0); } public void TestClassInfoClassCleanupMethodShouldInvokeWhenTestClassInitializedIsCalled() @@ -144,8 +144,8 @@ public void TestClassInfoClassCleanupMethodShouldInvokeWhenTestClassInitializedI public void TestClassInfoClassCleanupMethodShouldInvokeBaseClassCleanupMethodWhenTestClassInitializedIsCalled() { - int classcleanupCallCount = 0; - DummyBaseTestClass.ClassCleanupMethodBody = () => classcleanupCallCount++; + int classCleanupCallCount = 0; + DummyBaseTestClass.ClassCleanupMethodBody = () => classCleanupCallCount++; _testClassInfo.BaseClassInitAndCleanupMethods.Enqueue( new Tuple( @@ -156,7 +156,7 @@ public void TestClassInfoClassCleanupMethodShouldInvokeBaseClassCleanupMethodWhe string ret = _testClassInfo.RunClassCleanup(); Verify(ret is null); - Verify(classcleanupCallCount == 1); + Verify(classCleanupCallCount == 1); } public void TestClassInfoHasExecutableCleanupMethodShouldReturnFalseIfClassDoesNotHaveCleanupMethod() => Verify(!_testClassInfo.HasExecutableCleanupMethod); @@ -267,7 +267,7 @@ public void RunClassInitializeShouldSetClassInitializationExceptionOnException() DummyTestClass.ClassInitializeMethodBody = tc => UTF.Assert.Inconclusive("Test Inconclusive"); _testClassInfo.ClassInitializeMethod = typeof(DummyTestClass).GetMethod("ClassInitializeMethod"); - Exception exception = VerifyThrows(() => _testClassInfo.RunClassInitialize(_testContext)); + VerifyThrows(() => _testClassInfo.RunClassInitialize(_testContext)); Verify(_testClassInfo.ClassInitializationException is not null); } @@ -462,8 +462,8 @@ public void RunClassInitializeShouldThrowTheInnerMostExceptionWhenThereAreMultip public void RunClassCleanupShouldInvokeIfClassCleanupMethod() { // Arrange - int classcleanupCallCount = 0; - DummyTestClass.ClassCleanupMethodBody = () => classcleanupCallCount++; + int classCleanupCallCount = 0; + DummyTestClass.ClassCleanupMethodBody = () => classCleanupCallCount++; _testClassInfo.ClassCleanupMethod = typeof(DummyTestClass).GetMethod(nameof(DummyTestClass.ClassCleanupMethod)); // Act @@ -471,14 +471,14 @@ public void RunClassCleanupShouldInvokeIfClassCleanupMethod() // Assert Verify(classCleanup is null); - Verify(classcleanupCallCount == 1); + Verify(classCleanupCallCount == 1); } public void RunClassCleanupShouldNotInvokeIfClassCleanupIsNull() { // Arrange - int classcleanupCallCount = 0; - DummyTestClass.ClassCleanupMethodBody = () => classcleanupCallCount++; + int classCleanupCallCount = 0; + DummyTestClass.ClassCleanupMethodBody = () => classCleanupCallCount++; _testClassInfo.ClassCleanupMethod = null; // Act @@ -486,7 +486,7 @@ public void RunClassCleanupShouldNotInvokeIfClassCleanupIsNull() // Assert Verify(classCleanup is null); - Verify(classcleanupCallCount == 0); + Verify(classCleanupCallCount == 0); } public void RunClassCleanupShouldReturnAssertFailureExceptionDetails() @@ -555,8 +555,8 @@ public void RunBaseClassCleanupWithNoDerivedClassCleanupShouldReturnExceptionDet public void RunBaseClassCleanupEvenIfThereIsNoDerivedClassCleanup() { // Arrange - int classcleanupCallCount = 0; - DummyBaseTestClass.ClassCleanupMethodBody = () => classcleanupCallCount++; + int classCleanupCallCount = 0; + DummyBaseTestClass.ClassCleanupMethodBody = () => classCleanupCallCount++; MethodInfo baseClassCleanupMethod = typeof(DummyBaseTestClass).GetMethod(nameof(DummyBaseTestClass.CleanupClassMethod)); _testClassInfo.ClassCleanupMethod = null; _testClassInfo.BaseClassInitAndCleanupMethods.Enqueue(Tuple.Create((MethodInfo)null, baseClassCleanupMethod)); @@ -568,16 +568,15 @@ public void RunBaseClassCleanupEvenIfThereIsNoDerivedClassCleanup() // Assert Verify(_testClassInfo.HasExecutableCleanupMethod); Verify(classCleanup is null); - Verify(classcleanupCallCount == 1, "DummyBaseTestClass.CleanupClassMethod call count"); + Verify(classCleanupCallCount == 1, "DummyBaseTestClass.CleanupClassMethod call count"); } public void RunClassCleanupShouldThrowTheInnerMostExceptionWhenThereAreMultipleNestedTypeInitializationExceptions() { - DummyTestClass.ClassCleanupMethodBody = () => - // This helper calls inner helper, and the inner helper ctor throws. - // We want to see the real exception on screen, and not TypeInitializationException - // which has no info about what failed. - FailingStaticHelper.DoWork(); + // This helper calls inner helper, and the inner helper ctor throws. + // We want to see the real exception on screen, and not TypeInitializationException + // which has no info about what failed. + DummyTestClass.ClassCleanupMethodBody = FailingStaticHelper.DoWork; _testClassInfo.ClassCleanupMethod = typeof(DummyTestClass).GetMethod("ClassCleanupMethod"); string errorMessage = _testClassInfo.RunClassCleanup(); @@ -646,9 +645,7 @@ public class DummyTestClass public static void ClassCleanupMethod() => ClassCleanupMethodBody?.Invoke(); } - private class DummyTestClassAttribute : UTF.TestClassAttribute - { - } + private class DummyTestClassAttribute : UTF.TestClassAttribute; private static class FailingStaticHelper { diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs index 56f2e1461d..afdb758869 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs @@ -259,15 +259,17 @@ public void RunTestsForTestShouldPassInTestRunParametersInformationAsPropertiesT TestCase[] tests = [testCase]; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - True - - - - - - "); + """ + + + True + + + + + + + """); _testExecutionManager.RunTests(tests, _runContext, _frameworkHandle, new TestRunCancellationToken()); @@ -278,16 +280,18 @@ public void RunTestsForTestShouldPassInTestRunParametersInformationAsPropertiesT public void RunTestsForTestShouldPassInTcmPropertiesAsPropertiesToTheTest() { TestCase testCase = GetTestCase(typeof(DummyTestClass), "PassingTest"); - object[] propertiesValue = new object[] { 32, 534, 5, "sample build directory", "sample build flavor", "132456", "sample build platform", "http://sampleBuildUti/", "http://samplecollectionuri/", "sample team project", false, 1401, 54, "sample configuration name", 345 }; + object[] propertiesValue = [32, 534, 5, "sample build directory", "sample build flavor", "132456", "sample build platform", "http://sampleBuildUti/", "http://samplecollectionuri/", "sample team project", false, 1401, 54, "sample configuration name", 345]; SetTestCaseProperties(testCase, propertiesValue); TestCase[] tests = [testCase]; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - True - - "); + """ + + + True + + + """); _testExecutionManager.RunTests(tests, _runContext, _frameworkHandle, new TestRunCancellationToken()); @@ -313,30 +317,34 @@ public void RunTestsShouldClearSessionParametersAcrossRuns() TestCase[] tests = [testCase]; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - True - - - - - - "); + """ + + + True + + + + + + + """); // Trigger First Run _testExecutionManager.RunTests(tests, _runContext, _frameworkHandle, new TestRunCancellationToken()); // Update runsettings to have different values for similar keys _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - True - - - - - - "); + """ + + + True + + + + + + + """); // Trigger another Run _testExecutionManager.RunTests(tests, _runContext, _frameworkHandle, new TestRunCancellationToken()); @@ -366,12 +374,14 @@ private void RunTestsForSourceShouldPassInTestRunParametersInformationAsProperti var sources = new List { Assembly.GetExecutingAssembly().Location }; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - - - - "); + """ + + + + + + + """); _testExecutionManager.RunTests(sources, _runContext, _frameworkHandle, _cancellationToken); @@ -412,7 +422,7 @@ public void SendTestResultsShouldFillInDataRowIndexIfTestIsDataDriven() var testCase = new TestCase("DummyTest", new System.Uri("executor://testExecutor"), Assembly.GetExecutingAssembly().Location); UnitTestResult unitTestResult1 = new() { DatarowIndex = 0, DisplayName = "DummyTest" }; UnitTestResult unitTestResult2 = new() { DatarowIndex = 1, DisplayName = "DummyTest" }; - _testExecutionManager.SendTestResults(testCase, new UnitTestResult[] { unitTestResult1, unitTestResult2 }, default, default, _frameworkHandle); + _testExecutionManager.SendTestResults(testCase, [unitTestResult1, unitTestResult2], default, default, _frameworkHandle); Verify(_frameworkHandle.TestDisplayNameList[0] == "DummyTest (Data Row 0)"); Verify(_frameworkHandle.TestDisplayNameList[1] == "DummyTest (Data Row 1)"); } @@ -430,16 +440,18 @@ public void RunTestsForTestShouldRunTestsInParallelWhenEnabledInRunsettings() TestCase[] tests = [testCase11, testCase12, testCase21, testCase22]; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - True - - - - 2 - - - "); + """ + + + True + + + + 2 + + + + """); try { @@ -464,17 +476,19 @@ public void RunTestsForTestShouldRunTestsByMethodLevelWhenSpecified() TestCase[] tests = [testCase11, testCase12]; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - True - - - - 2 - MethodLevel - - - "); + """ + + + True + + + + 2 + MethodLevel + + + + """); try { @@ -500,16 +514,18 @@ public void RunTestsForTestShouldRunTestsWithSpecifiedNumberOfWorkers() TestCase[] tests = [testCase1, testCase2, testCase3]; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - True - - - - 3 - - - "); + """ + + + True + + + + 3 + + + + """); try { @@ -536,12 +552,15 @@ public void RunTestsForTestShouldNotRunTestsInParallelWhenDisabledFromRunsetting TestCase testCase2 = GetTestCase(typeof(DummyTestClassForParallelize), "TestMethod2"); TestCase[] tests = [testCase1, testCase2]; - _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - true - - "); + _runContext.MockRunSettings.Setup(rs => rs.SettingsXml) + .Returns( + """ + + + true + + + """); try { @@ -554,7 +573,7 @@ public void RunTestsForTestShouldNotRunTestsInParallelWhenDisabledFromRunsetting testablePlatformService.MockReflectionOperations.Setup( ro => ro.GetCustomAttributes(It.IsAny(), It.IsAny())). Returns((Assembly asm, Type type) => type.FullName.Equals(typeof(ParallelizeAttribute).FullName, StringComparison.Ordinal) - ? (object[])[new ParallelizeAttribute { Workers = 10, Scope = ExecutionScope.MethodLevel }] + ? [new ParallelizeAttribute { Workers = 10, Scope = ExecutionScope.MethodLevel }] : originalReflectionOperation.GetCustomAttributes(asm, type)); testablePlatformService.MockReflectionOperations.Setup( @@ -578,14 +597,16 @@ public void RunTestsForTestShouldNotRunTestsInParallelWhenDisabledFromSource() TestCase[] tests = [testCase1, testCase2]; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - - 2 - MethodLevel - - - "); + """ + + + + 2 + MethodLevel + + + + """); try { @@ -598,7 +619,7 @@ public void RunTestsForTestShouldNotRunTestsInParallelWhenDisabledFromSource() testablePlatformService.MockReflectionOperations.Setup( ro => ro.GetCustomAttributes(It.IsAny(), It.IsAny())). Returns((Assembly asm, Type type) => type.FullName.Equals(typeof(DoNotParallelizeAttribute).FullName, StringComparison.Ordinal) - ? (object[])[new DoNotParallelizeAttribute()] + ? [new DoNotParallelizeAttribute()] : originalReflectionOperation.GetCustomAttributes(asm, type)); testablePlatformService.MockReflectionOperations.Setup( @@ -627,17 +648,19 @@ public void RunTestsForTestShouldRunNonParallelizableTestsSeparately() TestCase[] tests = [testCase1, testCase2, testCase3, testCase4]; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - True - - - - 2 - MethodLevel - - - "); + """ + + + True + + + + 2 + MethodLevel + + + + """); try { @@ -662,14 +685,16 @@ public void RunTestsForTestShouldPreferParallelSettingsFromRunSettingsOverAssemb TestCase[] tests = [testCase1, testCase2]; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - - 2 - MethodLevel - - - "); + """ + + + + 2 + MethodLevel + + + + """); try { @@ -682,7 +707,7 @@ public void RunTestsForTestShouldPreferParallelSettingsFromRunSettingsOverAssemb testablePlatformService.MockReflectionOperations.Setup( ro => ro.GetCustomAttributes(It.IsAny(), It.IsAny())). Returns((Assembly asm, Type type) => type.FullName.Equals(typeof(ParallelizeAttribute).FullName, StringComparison.Ordinal) - ? (object[])[new ParallelizeAttribute { Workers = 1 }] + ? [new ParallelizeAttribute { Workers = 1 }] : originalReflectionOperation.GetCustomAttributes(asm, type)); testablePlatformService.MockReflectionOperations.Setup( @@ -711,19 +736,20 @@ private void RunTestsForTestShouldRunTestsInTheParentDomainsApartmentState() TestCase testCase3 = GetTestCase(typeof(DummyTestClassWithDoNotParallelizeMethods), "TestMethod3"); TestCase testCase4 = GetTestCase(typeof(DummyTestClassWithDoNotParallelizeMethods), "TestMethod4"); - ////testCase3.SetPropertyValue(MSTest.TestAdapter.Constants.DoNotParallelizeProperty, true); testCase4.SetPropertyValue(TestAdapterConstants.DoNotParallelizeProperty, true); TestCase[] tests = [testCase1, testCase2, testCase3, testCase4]; _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( - @" - - - 3 - MethodLevel - - - "); + """ + + + + 3 + MethodLevel + + + + """); try { @@ -972,9 +998,7 @@ public void TestMethod4() } } - private class DummyTestClassAttribute : TestClassAttribute - { - } + private class DummyTestClassAttribute : TestClassAttribute; #endregion } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodFilterTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodFilterTests.cs index 21a63597cd..6d2b2fa7d1 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodFilterTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodFilterTests.cs @@ -243,7 +243,5 @@ private sealed class TestableTestCaseFilterExpression : ITestCaseFilterExpressio public bool MatchTestCase(TestCase testCase, Func propertyValueProvider) => throw new NotImplementedException(); } - private class DummyTestClassAttribute : UTF.TestClassAttribute - { - } + private class DummyTestClassAttribute : UTF.TestClassAttribute; } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodInfoTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodInfoTests.cs index 3354eda997..98b5ca0879 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodInfoTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodInfoTests.cs @@ -50,7 +50,7 @@ public class TestMethodInfoTests : TestContainer public TestMethodInfoTests() { - _constructorInfo = typeof(DummyTestClass).GetConstructors().Single(); + _constructorInfo = typeof(DummyTestClass).GetConstructor([])!; _methodInfo = typeof(DummyTestClass).GetMethods().Single(m => m.Name.Equals("DummyTestMethod", StringComparison.Ordinal)); _classAttribute = new UTF.TestClassAttribute(); _testMethodAttribute = new UTF.TestMethodAttribute(); @@ -211,6 +211,31 @@ public void TestMethodInfoInvokeShouldClearTestContextMessagesAfterReporting() Verify(result.TestContextMessages.Contains("SeaShore")); } + public void Invoke_WhenTestMethodThrowsMissingMethodException_TestOutcomeIsFailedAndExceptionIsPreserved() + { + DummyTestClass.TestMethodBody = _ => + { + var input = new + { + Field1 = "StringWith\0Null", + Field2 = "NormalString", + }; + + Activator.CreateInstance(input.GetType()); + }; + + var method = new TestMethodInfo( + _methodInfo, + _testClassInfo, + _testMethodOptions); + + UTF.TestResult result = method.Invoke(null); + + Verify(result.Outcome == UTF.UnitTestOutcome.Failed); + Verify(result.TestFailureException is TestFailedException); + Verify(result.TestFailureException.InnerException is MissingMethodException); + } + #endregion #region TestClass constructor setup @@ -274,9 +299,10 @@ public void TestMethodInfoInvokeShouldSetStackTraceInformationIfTestClassConstru var exception = _testMethodInfo.Invoke(null).TestFailureException as TestFailedException; Verify(exception is not null); + Verify(exception.StackTraceInformation is not null); Verify( - (bool)exception?.StackTraceInformation.ErrorStackTrace.StartsWith( - " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests.<>c.b__", StringComparison.Ordinal)); + exception.StackTraceInformation.ErrorStackTrace.StartsWith( + " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests.<>c.b__", StringComparison.Ordinal)); } public void TestMethodInfoInvokeShouldSetStackTraceInformationIfTestClassConstructorThrowsWithoutInnerException() @@ -288,8 +314,9 @@ public void TestMethodInfoInvokeShouldSetStackTraceInformationIfTestClassConstru var exception = method.Invoke(null).TestFailureException as TestFailedException; Verify(exception is not null); + Verify(exception.StackTraceInformation is not null); Verify( - (bool)exception?.StackTraceInformation.ErrorStackTrace.StartsWith( + exception.StackTraceInformation.ErrorStackTrace.StartsWith( " at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)", StringComparison.Ordinal)); } @@ -378,9 +405,10 @@ public void TestMethodInfoInvokeShouldSetStackTraceInformationIfSetTestContextTh var exception = _testMethodInfo.Invoke(null).TestFailureException as TestFailedException; Verify(exception is not null); + Verify(exception.StackTraceInformation is not null); Verify( - (bool)exception?.StackTraceInformation.ErrorStackTrace.StartsWith( - " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests.<>c.b__", StringComparison.Ordinal)); + exception.StackTraceInformation.ErrorStackTrace.StartsWith( + " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests.<>c.b__", StringComparison.Ordinal)); } #endregion @@ -648,15 +676,6 @@ public void TestMethodInfoInvokeShouldNotThrowIfTestCleanupMethodIsNull() Verify(result.Outcome == UTF.UnitTestOutcome.Passed); } - public void TestMethodInfoInvokeShouldNotThrowIfTestCleanupMethodForBaseClassIsNull() - { - _testClassInfo.BaseTestCleanupMethodsQueue.Enqueue(null); - - UTF.TestResult result = _testMethodInfo.Invoke(null); - - Verify(result.Outcome == UTF.UnitTestOutcome.Passed); - } - public void TestMethodInfoInvokeShouldCallTestCleanupForBaseTestClasses() { var callOrder = new List(); @@ -848,7 +867,6 @@ public void TestMethodInfoInvokeShouldSetMoreImportantOutcomeIfTestCleanupIsInco _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod"); UTF.TestResult result = _testMethodInfo.Invoke(null); - var exception = result.TestFailureException as TestFailedException; Verify(result.Outcome == UTF.UnitTestOutcome.Failed); } @@ -857,7 +875,7 @@ public void TestMethodInfoInvokeShouldCallDisposeForDisposableTestClass() { bool disposeCalled = false; DummyTestClassWithDisposable.DisposeMethodBody = () => disposeCalled = true; - ConstructorInfo ctorInfo = typeof(DummyTestClassWithDisposable).GetConstructors().Single(); + ConstructorInfo ctorInfo = typeof(DummyTestClassWithDisposable).GetConstructor([])!; var testClass = new TestClassInfo(typeof(DummyTestClassWithDisposable), ctorInfo, null, _classAttribute, _testAssemblyInfo); var method = new TestMethodInfo(typeof(DummyTestClassWithDisposable).GetMethod("DummyTestMethod"), testClass, _testMethodOptions); @@ -872,7 +890,7 @@ public void TestMethodInfoInvoke_WhenTestClassIsAsyncDisposable_ShouldDisposeAsy // Arrange bool asyncDisposeCalled = false; DummyTestClassWithAsyncDisposable.DisposeAsyncMethodBody = () => asyncDisposeCalled = true; - ConstructorInfo ctorInfo = typeof(DummyTestClassWithAsyncDisposable).GetConstructors().Single(); + ConstructorInfo ctorInfo = typeof(DummyTestClassWithAsyncDisposable).GetConstructor([])!; var testClass = new TestClassInfo(typeof(DummyTestClassWithAsyncDisposable), ctorInfo, null, _classAttribute, _testAssemblyInfo); var method = new TestMethodInfo(typeof(DummyTestClassWithAsyncDisposable).GetMethod("DummyTestMethod"), testClass, _testMethodOptions); @@ -893,7 +911,7 @@ public void TestMethodInfoInvoke_WhenTestClassIsDisposableAndAsyncDisposable_Sho DummyTestClassWithAsyncDisposableAndDisposable.DisposeMethodBody = () => disposeCalledOrder = ++order; DummyTestClassWithAsyncDisposableAndDisposable.DisposeAsyncMethodBody = () => disposeAsyncCalledOrder = ++order; - ConstructorInfo ctorInfo = typeof(DummyTestClassWithAsyncDisposableAndDisposable).GetConstructors().Single(); + ConstructorInfo ctorInfo = typeof(DummyTestClassWithAsyncDisposableAndDisposable).GetConstructor([])!; var testClass = new TestClassInfo(typeof(DummyTestClassWithAsyncDisposableAndDisposable), ctorInfo, null, _classAttribute, _testAssemblyInfo); var method = new TestMethodInfo(typeof(DummyTestClassWithAsyncDisposableAndDisposable).GetMethod("DummyTestMethod"), testClass, _testMethodOptions); @@ -911,7 +929,7 @@ public void TestMethodInfoInvokeShouldCallDisposeForDisposableTestClassIfTestCle bool disposeCalled = false; DummyTestClassWithDisposable.DisposeMethodBody = () => disposeCalled = true; DummyTestClassWithDisposable.DummyTestCleanupMethodBody = classInstance => throw new NotImplementedException(); - ConstructorInfo ctorInfo = typeof(DummyTestClassWithDisposable).GetConstructors().Single(); + ConstructorInfo ctorInfo = typeof(DummyTestClassWithDisposable).GetConstructor([])!; var testClass = new TestClassInfo(typeof(DummyTestClassWithDisposable), ctorInfo, null, _classAttribute, _testAssemblyInfo) { TestCleanupMethod = typeof(DummyTestClassWithDisposable).GetMethod("DummyTestCleanupMethod"), diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs index 31e659ac41..326b45980b 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs @@ -7,6 +7,7 @@ using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; +using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; @@ -41,7 +42,7 @@ public class TestMethodRunnerTests : TestContainer public TestMethodRunnerTests() { - ConstructorInfo constructorInfo = typeof(DummyTestClass).GetConstructors().Single(); + ConstructorInfo constructorInfo = typeof(DummyTestClass).GetConstructor([])!; _methodInfo = typeof(DummyTestClass).GetMethods().Single(m => m.Name.Equals("DummyTestMethod", StringComparison.Ordinal)); var classAttribute = new UTF.TestClassAttribute(); _testMethodAttribute = new UTF.TestMethodAttribute(); @@ -89,6 +90,8 @@ public TestMethodRunnerTests() _testablePlatformServiceProvider = new TestablePlatformServiceProvider(); _testablePlatformServiceProvider.SetupMockReflectionOperations(); PlatformServiceProvider.Instance = _testablePlatformServiceProvider; + + ReflectHelper.Instance.ClearCache(); } protected override void Dispose(bool disposing) @@ -110,7 +113,7 @@ public void ExecuteForAssemblyInitializeThrowingExceptionShouldReturnUnitTestRes BindingFlags.Static | BindingFlags.NonPublic), }; - ConstructorInfo constructorInfo = typeof(DummyTestClass).GetConstructors().Single(); + ConstructorInfo constructorInfo = typeof(DummyTestClass).GetConstructor([])!; var classAttribute = new UTF.TestClassAttribute(); PropertyInfo testContextProperty = typeof(DummyTestClass).GetProperty("TestContext"); @@ -136,7 +139,7 @@ public void ExecuteForClassInitializeThrowingExceptionShouldReturnUnitTestResult // Arrange. var tai = new TestAssemblyInfo(typeof(DummyTestClass).Assembly); - ConstructorInfo constructorInfo = typeof(DummyTestClass).GetConstructors().Single(); + ConstructorInfo constructorInfo = typeof(DummyTestClass).GetConstructor([])!; var classAttribute = new UTF.TestClassAttribute(); PropertyInfo testContextProperty = typeof(DummyTestClass).GetProperty("TestContext"); @@ -306,8 +309,8 @@ public void RunTestMethodShouldRunDataDrivenTestsWhenDataIsProvidedUsingDataSour TestDataSource testDataSource = new(); // Setup mocks - _testablePlatformServiceProvider.MockReflectionOperations.Setup(rf => rf.GetCustomAttributes(_methodInfo, It.IsAny(), It.IsAny())).Returns(attributes); - _testablePlatformServiceProvider.MockTestDataSource.Setup(tds => tds.GetData(testMethodInfo, _testContextImplementation)).Returns(new object[] { 1, 2, 3 }); + _testablePlatformServiceProvider.MockReflectionOperations.Setup(rf => rf.GetCustomAttributes(_methodInfo, It.IsAny())).Returns(attributes); + _testablePlatformServiceProvider.MockTestDataSource.Setup(tds => tds.GetData(testMethodInfo, _testContextImplementation)).Returns([1, 2, 3]); UnitTestResult[] results = testMethodRunner.RunTestMethod(); @@ -352,8 +355,8 @@ public void RunTestMethodShouldSetDataRowIndexForDataDrivenTestsWhenDataIsProvid var attributes = new Attribute[] { dataSourceAttribute }; // Setup mocks - _testablePlatformServiceProvider.MockReflectionOperations.Setup(rf => rf.GetCustomAttributes(_methodInfo, It.IsAny(), It.IsAny())).Returns(attributes); - _testablePlatformServiceProvider.MockTestDataSource.Setup(tds => tds.GetData(testMethodInfo, _testContextImplementation)).Returns(new object[] { 1, 2, 3 }); + _testablePlatformServiceProvider.MockReflectionOperations.Setup(rf => rf.GetCustomAttributes(_methodInfo, It.IsAny())).Returns(attributes); + _testablePlatformServiceProvider.MockTestDataSource.Setup(tds => tds.GetData(testMethodInfo, _testContextImplementation)).Returns([1, 2, 3]); UnitTestResult[] results = testMethodRunner.RunTestMethod(); @@ -363,7 +366,7 @@ public void RunTestMethodShouldSetDataRowIndexForDataDrivenTestsWhenDataIsProvid Verify(results[2].DatarowIndex == 2); } - public void RunTestMethodShoudlRunOnlyDataSourceTestsWhenBothDataSourceAndDataRowAreProvided() + public void RunTestMethodShouldRunOnlyDataSourceTestsWhenBothDataSourceAndDataRowAreProvided() { var testMethodInfo = new TestableTestmethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new UTF.TestResult()); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation, false); @@ -378,8 +381,8 @@ public void RunTestMethodShoudlRunOnlyDataSourceTestsWhenBothDataSourceAndDataRo var attributes = new Attribute[] { dataSourceAttribute, dataRowAttribute }; // Setup mocks - _testablePlatformServiceProvider.MockReflectionOperations.Setup(rf => rf.GetCustomAttributes(_methodInfo, It.IsAny(), It.IsAny())).Returns(attributes); - _testablePlatformServiceProvider.MockTestDataSource.Setup(tds => tds.GetData(testMethodInfo, _testContextImplementation)).Returns(new object[] { 1, 2, 3 }); + _testablePlatformServiceProvider.MockReflectionOperations.Setup(rf => rf.GetCustomAttributes(_methodInfo, It.IsAny())).Returns(attributes); + _testablePlatformServiceProvider.MockTestDataSource.Setup(tds => tds.GetData(testMethodInfo, _testContextImplementation)).Returns([1, 2, 3]); UnitTestResult[] results = testMethodRunner.RunTestMethod(); @@ -405,7 +408,7 @@ public void RunTestMethodShouldFillInDisplayNameWithDataRowDisplayNameIfProvided var attributes = new Attribute[] { dataRowAttribute }; // Setup mocks - _testablePlatformServiceProvider.MockReflectionOperations.Setup(ro => ro.GetCustomAttributes(_methodInfo, It.IsAny(), It.IsAny())).Returns(attributes); + _testablePlatformServiceProvider.MockReflectionOperations.Setup(ro => ro.GetCustomAttributes(_methodInfo, It.IsAny())).Returns(attributes); UnitTestResult[] results = testMethodRunner.RunTestMethod(); @@ -428,12 +431,12 @@ public void RunTestMethodShouldFillInDisplayNameWithDataRowArgumentsIfNoDisplayN var attributes = new Attribute[] { dataRowAttribute }; // Setup mocks - _testablePlatformServiceProvider.MockReflectionOperations.Setup(rf => rf.GetCustomAttributes(_methodInfo, It.IsAny(), It.IsAny())).Returns(attributes); + _testablePlatformServiceProvider.MockReflectionOperations.Setup(rf => rf.GetCustomAttributes(_methodInfo, It.IsAny())).Returns(attributes); UnitTestResult[] results = testMethodRunner.RunTestMethod(); Verify(results.Length == 1); - Verify(results[0].DisplayName == "DummyTestMethod (2,DummyString)"); + Verify(results[0].DisplayName == "DummyTestMethod (2,\"DummyString\")"); } public void RunTestMethodShouldSetResultFilesIfPresentForDataDrivenTests() @@ -454,7 +457,7 @@ public void RunTestMethodShouldSetResultFilesIfPresentForDataDrivenTests() var attributes = new Attribute[] { dataRowAttribute1, dataRowAttribute2 }; // Setup mocks - _testablePlatformServiceProvider.MockReflectionOperations.Setup(rf => rf.GetCustomAttributes(_methodInfo, It.IsAny(), It.IsAny())).Returns(attributes); + _testablePlatformServiceProvider.MockReflectionOperations.Setup(rf => rf.GetCustomAttributes(_methodInfo, It.IsAny())).Returns(attributes); UnitTestResult[] results = testMethodRunner.RunTestMethod(); Verify(results[0].ResultFiles.ToList().Contains("C:\\temp.txt")); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TypeCacheTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TypeCacheTests.cs index b143c20a61..9737d3f08b 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TypeCacheTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TypeCacheTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Globalization; @@ -10,6 +10,7 @@ using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -38,6 +39,8 @@ public TypeCacheTests() _testablePlatformServiceProvider = new TestablePlatformServiceProvider(); PlatformServiceProvider.Instance = _testablePlatformServiceProvider; + ReflectHelper.Instance.ClearCache(); + SetupMocks(); } @@ -177,7 +180,7 @@ public void GetTestMethodInfoShouldSetTestContextIfPresent() var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, @@ -195,7 +198,7 @@ public void GetTestMethodInfoShouldSetTestContextToNullIfNotPresent() var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, @@ -215,7 +218,7 @@ public void GetTestMethodInfoShouldAddAssemblyInfoToTheCache() var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -232,10 +235,10 @@ public void GetTestMethodInfoShouldNotThrowIfWeFailToDiscoverTypeFromAnAssembly( var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(It.IsAny(), true)).Throws(new Exception()); + rh => rh.IsNonDerivedAttributeDefined(It.IsAny(), true)).Throws(new Exception()); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(typeof(DummyTestClassWithTestMethods), true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(typeof(DummyTestClassWithTestMethods), true)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -251,10 +254,10 @@ public void GetTestMethodInfoShouldCacheAssemblyInitializeAttribute() var testMethod = new TestMethod("TestInit", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetTypeInfo(), true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type.GetTypeInfo(), true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -271,10 +274,10 @@ public void GetTestMethodInfoShouldCacheAssemblyCleanupAttribute() var testMethod = new TestMethod("TestCleanup", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetTypeInfo(), true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type.GetTypeInfo(), true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -291,12 +294,12 @@ public void GetTestMethodInfoShouldCacheAssemblyInitAndCleanupAttribute() var testMethod = new TestMethod("TestInitOrCleanup", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetTypeInfo(), true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type.GetTypeInfo(), true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -314,10 +317,10 @@ public void GetTestMethodInfoShouldThrowIfAssemblyInitHasIncorrectSignature() var testMethod = new TestMethod("M", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetTypeInfo(), true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type.GetTypeInfo(), true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); void A() => _typeCache.GetTestMethodInfo( @@ -346,10 +349,10 @@ public void GetTestMethodInfoShouldThrowIfAssemblyCleanupHasIncorrectSignature() var testMethod = new TestMethod("M", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetTypeInfo(), true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); void A() => _typeCache.GetTestMethodInfo( @@ -379,7 +382,7 @@ public void GetTestMethodInfoShouldCacheAssemblyInfoInstanceAndReuseTheCache() var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetTypeInfo(), true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type.GetTypeInfo(), true)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -391,7 +394,7 @@ public void GetTestMethodInfoShouldCacheAssemblyInfoInstanceAndReuseTheCache() new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null, "test"), new Dictionary()), false); - _mockReflectHelper.Verify(rh => rh.IsAttributeDefined(type.GetTypeInfo(), true), Times.Once); + _mockReflectHelper.Verify(rh => rh.IsDerivedAttributeDefined(type.GetTypeInfo(), true), Times.Once); Verify(_typeCache.AssemblyInfoCache.Count == 1); } @@ -406,7 +409,7 @@ public void GetTestMethodInfoShouldAddClassInfoToTheCache() var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -424,10 +427,10 @@ public void GetTestMethodInfoShouldCacheClassInitializeAttribute() var testMethod = new TestMethod("TestInit", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -447,16 +450,16 @@ public void GetTestMethodInfoShouldCacheBaseClassInitializeAttributes() var testMethod = new TestMethod("TestMethod", type.FullName, "A", false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(baseType.GetMethod("AssemblyInit"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(baseType.GetMethod("AssemblyInit"), false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.GetCustomAttribute(baseType.GetMethod("AssemblyInit"))) + rh => rh.GetFirstDerivedAttributeOrDefault(baseType.GetMethod("AssemblyInit"), true)) .Returns(new UTF.ClassInitializeAttribute(UTF.InheritanceBehavior.BeforeEachDerivedClass)); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("ClassInit"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("ClassInit"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -475,10 +478,10 @@ public void GetTestMethodInfoShouldCacheClassCleanupAttribute() var testMethod = new TestMethod("TestCleanup", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -497,11 +500,11 @@ public void GetTestMethodInfoShouldCacheBaseClassCleanupAttributes() var testMethod = new TestMethod("TestMethod", type.FullName, "A", false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(baseType.GetMethod("AssemblyCleanup"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(baseType.GetMethod("AssemblyCleanup"), false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.GetCustomAttribute(baseType.GetMethod("AssemblyCleanup"))) + rh => rh.GetFirstDerivedAttributeOrDefault(baseType.GetMethod("AssemblyCleanup"), true)) .Returns(new UTF.ClassCleanupAttribute(UTF.InheritanceBehavior.BeforeEachDerivedClass)); _typeCache.GetTestMethodInfo( @@ -521,11 +524,11 @@ public void GetTestMethodInfoShouldCacheClassInitAndCleanupAttribute() var testMethod = new TestMethod("TestInitOrCleanup", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -547,23 +550,23 @@ public void GetTestMethodInfoShouldCacheBaseClassInitAndCleanupAttributes() MethodInfo baseCleanupMethod = baseType.GetMethod("ClassCleanup"); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(baseInitializeMethod, false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(baseInitializeMethod, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.GetCustomAttribute(baseInitializeMethod)) + rh => rh.GetFirstDerivedAttributeOrDefault(baseInitializeMethod, true)) .Returns(new UTF.ClassInitializeAttribute(UTF.InheritanceBehavior.BeforeEachDerivedClass)); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(baseCleanupMethod, false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(baseCleanupMethod, false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.GetCustomAttribute(baseCleanupMethod)) + rh => rh.GetFirstDerivedAttributeOrDefault(baseCleanupMethod, true)) .Returns(new UTF.ClassCleanupAttribute(UTF.InheritanceBehavior.BeforeEachDerivedClass)); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -591,35 +594,35 @@ public void GetTestMethodInfoShouldCacheParentAndGrandparentClassInitAndCleanupA MethodInfo parentCleanupMethod = parentType.GetMethod("ChildClassCleanup"); _mockReflectHelper - .Setup(rh => rh.IsAttributeDefined(type, true)) + .Setup(rh => rh.IsNonDerivedAttributeDefined(type, true)) .Returns(true); // Setup grandparent class init/cleanup methods _mockReflectHelper - .Setup(rh => rh.IsAttributeDefined(grandparentInitMethod, false)) + .Setup(rh => rh.IsNonDerivedAttributeDefined(grandparentInitMethod, false)) .Returns(true); _mockReflectHelper - .Setup(rh => rh.GetCustomAttribute(grandparentInitMethod)) + .Setup(rh => rh.GetFirstDerivedAttributeOrDefault(grandparentInitMethod, true)) .Returns(new UTF.ClassInitializeAttribute(UTF.InheritanceBehavior.BeforeEachDerivedClass)); _mockReflectHelper - .Setup(rh => rh.IsAttributeDefined(grandparentCleanupMethod, false)) + .Setup(rh => rh.IsNonDerivedAttributeDefined(grandparentCleanupMethod, false)) .Returns(true); _mockReflectHelper - .Setup(rh => rh.GetCustomAttribute(grandparentCleanupMethod)) + .Setup(rh => rh.GetFirstDerivedAttributeOrDefault(grandparentCleanupMethod, true)) .Returns(new UTF.ClassCleanupAttribute(UTF.InheritanceBehavior.BeforeEachDerivedClass)); // Setup parent class init/cleanup methods _mockReflectHelper - .Setup(rh => rh.IsAttributeDefined(parentInitMethod, false)) + .Setup(rh => rh.IsNonDerivedAttributeDefined(parentInitMethod, false)) .Returns(true); _mockReflectHelper - .Setup(rh => rh.GetCustomAttribute(parentInitMethod)) + .Setup(rh => rh.GetFirstDerivedAttributeOrDefault(parentInitMethod, true)) .Returns(new UTF.ClassInitializeAttribute(UTF.InheritanceBehavior.BeforeEachDerivedClass)); _mockReflectHelper - .Setup(rh => rh.IsAttributeDefined(parentCleanupMethod, false)) + .Setup(rh => rh.IsNonDerivedAttributeDefined(parentCleanupMethod, false)) .Returns(true); _mockReflectHelper - .Setup(rh => rh.GetCustomAttribute(parentCleanupMethod)) + .Setup(rh => rh.GetFirstDerivedAttributeOrDefault(parentCleanupMethod, true)) .Returns(new UTF.ClassCleanupAttribute(UTF.InheritanceBehavior.BeforeEachDerivedClass)); var testMethod = new TestMethod("TestMethod", type.FullName, "A", isAsync: false); @@ -650,10 +653,10 @@ public void GetTestMethodInfoShouldThrowIfClassInitHasIncorrectSignature() var testMethod = new TestMethod("M", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); void A() => _typeCache.GetTestMethodInfo( @@ -682,10 +685,10 @@ public void GetTestMethodInfoShouldThrowIfClassCleanupHasIncorrectSignature() var testMethod = new TestMethod("M", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); void A() => _typeCache.GetTestMethodInfo( @@ -714,10 +717,10 @@ public void GetTestMethodInfoShouldCacheTestInitializeAttribute() var testMethod = new TestMethod("TestInit", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("TestInit"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("TestInit"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -734,10 +737,10 @@ public void GetTestMethodInfoShouldCacheTestCleanupAttribute() var testMethod = new TestMethod("TestCleanup", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("TestCleanup"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("TestCleanup"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -754,10 +757,10 @@ public void GetTestMethodInfoShouldThrowIfTestInitOrCleanupHasIncorrectSignature var testMethod = new TestMethod("M", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("TestInit"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("TestInit"), false)).Returns(true); void A() => _typeCache.GetTestMethodInfo( @@ -788,10 +791,10 @@ public void GetTestMethodInfoShouldCacheTestInitializeAttributeDefinedInBaseClas var testMethod = new TestMethod("TestMethod", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(baseType.GetMethod("TestInit"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(baseType.GetMethod("TestInit"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -809,10 +812,10 @@ public void GetTestMethodInfoShouldCacheTestCleanupAttributeDefinedInBaseClass() var testMethod = new TestMethod("TestMethod", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(baseType.GetMethod("TestCleanup"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(baseType.GetMethod("TestCleanup"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -830,7 +833,7 @@ public void GetTestMethodInfoShouldCacheClassInfoInstanceAndReuseFromCache() var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -882,6 +885,7 @@ public void GetTestMethodInfoShouldReturnTestMethodInfo() MethodInfo methodInfo = type.GetMethod("TestMethod"); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); + _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(It.IsAny(), false)).CallBase(); TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null, "test"), new Dictionary()), @@ -899,9 +903,9 @@ public void GetTestMethodInfoShouldReturnTestMethodInfoWithTimeout() MethodInfo methodInfo = type.GetMethod("TestMethodWithTimeout"); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); - _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(methodInfo, false)) + _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) .Returns(true); - + _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(It.IsAny(), false)).CallBase(); TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null, "test"), new Dictionary()), @@ -919,7 +923,7 @@ public void GetTestMethodInfoShouldThrowWhenTimeoutIsIncorrect() MethodInfo methodInfo = type.GetMethod("TestMethodWithIncorrectTimeout"); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); - _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(methodInfo, false)) + _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) .Returns(true); void A() => _typeCache.GetTestMethodInfo( @@ -945,11 +949,13 @@ void A() => _typeCache.GetTestMethodInfo( public void GetTestMethodInfoWhenTimeoutAttributeNotSetShouldReturnTestMethodInfoWithGlobalTimeout() { string runSettingsXml = - @" - - 4000 - - "; + """ + + + 4000 + + + """; MSTestSettings.PopulateSettings(MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias)); @@ -968,11 +974,13 @@ public void GetTestMethodInfoWhenTimeoutAttributeNotSetShouldReturnTestMethodInf public void GetTestMethodInfoWhenTimeoutAttributeSetShouldReturnTimeoutBasedOnAttributeEvenIfGlobalTimeoutSet() { string runSettingsXml = - @" - - 4000 - - "; + """ + + + 4000 + + + """; MSTestSettings.PopulateSettings(MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias)); @@ -980,7 +988,7 @@ public void GetTestMethodInfoWhenTimeoutAttributeSetShouldReturnTimeoutBasedOnAt MethodInfo methodInfo = type.GetMethod("TestMethodWithTimeout"); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); - _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(methodInfo, false)) + _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) .Returns(true); TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo( @@ -994,11 +1002,13 @@ public void GetTestMethodInfoWhenTimeoutAttributeSetShouldReturnTimeoutBasedOnAt public void GetTestMethodInfoForInvalidGLobalTimeoutShouldReturnTestMethodInfoWithTimeoutZero() { string runSettingsXml = - @" - - 30.5 - - "; + """ + + + 30.5 + + + """; MSTestSettings.PopulateSettings(MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias)); @@ -1020,6 +1030,7 @@ public void GetTestMethodInfoShouldReturnTestMethodInfoForMethodsAdornedWithADer MethodInfo methodInfo = type.GetMethod("TestMethodWithDerivedTestMethodAttribute"); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); + _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(It.IsAny(), false)).CallBase(); TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null, "test"), new Dictionary()), @@ -1135,9 +1146,10 @@ public void GetTestMethodInfoShouldNotAddDuplicateTestPropertiesToTestContext() public void GetTestMethodInfoShouldReturnTestMethodInfoForDerivedTestClasses() { Type type = typeof(DerivedTestClass); - MethodInfo methodInfo = type.GetRuntimeMethod("DummyTestMethod", Array.Empty()); + MethodInfo methodInfo = type.GetRuntimeMethod("DummyTestMethod", []); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); + _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(It.IsAny(), false)).CallBase(); TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null, "test"), new Dictionary()), @@ -1152,9 +1164,10 @@ public void GetTestMethodInfoShouldReturnTestMethodInfoForDerivedTestClasses() public void GetTestMethodInfoShouldReturnTestMethodInfoForDerivedClassMethodOverloadByDefault() { Type type = typeof(DerivedTestClass); - MethodInfo methodInfo = type.GetRuntimeMethod("OverloadedTestMethod", Array.Empty()); + MethodInfo methodInfo = type.GetRuntimeMethod("OverloadedTestMethod", []); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); + _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(It.IsAny(), false)).CallBase(); TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null, "test"), new Dictionary()), @@ -1170,12 +1183,13 @@ public void GetTestMethodInfoShouldReturnTestMethodInfoForDeclaringTypeMethodOve { Type baseType = typeof(BaseTestClass); Type type = typeof(DerivedTestClass); - MethodInfo methodInfo = baseType.GetRuntimeMethod("OverloadedTestMethod", Array.Empty()); + MethodInfo methodInfo = baseType.GetRuntimeMethod("OverloadedTestMethod", []); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false) { DeclaringClassFullName = baseType.FullName, }; + _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(It.IsAny(), false)).CallBase(); TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null, "test"), new Dictionary()), @@ -1209,10 +1223,10 @@ public void ClassInfoListWithExecutableCleanupMethodsShouldReturnEmptyListWhenCl var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("TestCleanup"), false)).Returns(false); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("TestCleanup"), false)).Returns(false); _typeCache.GetTestMethodInfo( testMethod, @@ -1231,10 +1245,10 @@ public void ClassInfoListWithExecutableCleanupMethodsShouldReturnClassInfosWithE var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -1265,10 +1279,10 @@ public void AssemblyInfoListWithExecutableCleanupMethodsShouldReturnEmptyListWhe var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(false); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(false); _typeCache.GetTestMethodInfo( testMethod, @@ -1287,10 +1301,10 @@ public void AssemblyInfoListWithExecutableCleanupMethodsShouldReturnAssemblyInfo var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetTypeInfo(), true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type.GetTypeInfo(), true)).Returns(true); _mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -1314,7 +1328,7 @@ public void ResolveExpectedExceptionHelperShouldReturnExpectedExceptionAttribute var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); UTF.ExpectedExceptionAttribute expectedException = new(typeof(DivideByZeroException)); - _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(methodInfo, false)) + _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) .Returns(true); _mockReflectHelper.Setup(rh => rh.ResolveExpectedExceptionHelper(methodInfo, testMethod)).Returns(expectedException); @@ -1332,7 +1346,7 @@ public void ResolveExpectedExceptionHelperShouldReturnNullIfExpectedExceptionAtt MethodInfo methodInfo = type.GetMethod("TestMethod"); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); - _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(methodInfo, false)) + _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) .Returns(true); TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo( @@ -1351,7 +1365,7 @@ public void ResolveExpectedExceptionHelperShouldThrowIfMultipleExpectedException MethodInfo methodInfo = type.GetMethod("TestMethodWithMultipleExpectedException"); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); - _mockReflectHelper.Setup(rh => rh.IsAttributeDefined(methodInfo, false)) + _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) .Returns(true); try @@ -1490,15 +1504,11 @@ public void TestMethod() [UTF.Ignore("IgnoreTestClassMessage")] [DummyTestClass] - internal class DummyTestClassWithIgnoreClassAndIgnoreTestWithMessage : DummyTestClassWithIgnoreTestWithMessage - { - } + internal class DummyTestClassWithIgnoreClassAndIgnoreTestWithMessage : DummyTestClassWithIgnoreTestWithMessage; [UTF.Ignore] [DummyTestClass] - internal class DummyTestClassWithIgnoreClassWithNoMessageAndIgnoreTestWithMessage : DummyTestClassWithIgnoreTestWithMessage - { - } + internal class DummyTestClassWithIgnoreClassWithNoMessageAndIgnoreTestWithMessage : DummyTestClassWithIgnoreTestWithMessage; [DummyTestClass] internal class DerivedTestClass : BaseTestClass @@ -1542,9 +1552,7 @@ private class DummyTestClassWithTestContextProperty : DummyTestClassWithIncorrec public new string TestContext { get; set; } } - private class DummyTestClassWithMultipleTestContextProperties : DummyTestClassWithTestContextProperty - { - } + private class DummyTestClassWithMultipleTestContextProperties : DummyTestClassWithTestContextProperty; [DummyTestClass] private class DummyTestClassWithInitializeMethods @@ -1677,13 +1685,9 @@ public static void TestMethod() } } - private class DerivedTestMethodAttribute : UTF.TestMethodAttribute - { - } + private class DerivedTestMethodAttribute : UTF.TestMethodAttribute; - private class DummyTestClassAttribute : UTF.TestClassAttribute - { - } + private class DummyTestClassAttribute : UTF.TestClassAttribute; #endregion } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestResultTest.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestResultTest.cs index f3a8c281c2..e2cbf4e355 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestResultTest.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestResultTest.cs @@ -54,10 +54,12 @@ public void ToTestResultShouldReturnConvertedTestResultWithFieldsSet() DateTimeOffset endTime = DateTimeOffset.Now; string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -104,10 +106,12 @@ public void ToTestResultForUniTestResultWithDebugTraceShouldReturnTestResultWith }; TestCase testCase = new("Foo", new Uri("Uri", UriKind.Relative), Assembly.GetExecutingAssembly().FullName); string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); var testResult = result.ToTestResult(testCase, DateTimeOffset.Now, DateTimeOffset.Now, "MachineName", adapterSettings); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestRunnerTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestRunnerTests.cs index 0ed6117ec1..6953eafcec 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestRunnerTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestRunnerTests.cs @@ -53,12 +53,14 @@ protected override void Dispose(bool disposing) public void ConstructorShouldPopulateSettings() { string runSettingsXml = - @" - - True - DummyPath\TestSettings1.testsettings - - "; + """ + + + True + DummyPath\TestSettings1.testsettings + + + """; _testablePlatformServiceProvider.MockSettingsProvider.Setup(sp => sp.Load(It.IsAny())) .Callback((XmlReader actualReader) => @@ -304,7 +306,7 @@ public void RunSingleTestShouldCallAssemblyInitializeAndClassInitializeMethodsIn _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) .Returns(Assembly.GetExecutingAssembly()); mockReflectHelper.Setup( - rh => rh.IsAttributeDefined(type.GetMethod("AssemblyInitialize"), It.IsAny())) + rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInitialize"), It.IsAny())) .Returns(true); int validator = 1; @@ -323,11 +325,13 @@ public void RunSingleTestShouldCallAssemblyInitializeAndClassInitializeMethodsIn private MSTestSettings GetSettingsWithDebugTrace(bool captureDebugTraceValue) { string runSettingsXml = - @" - - " + captureDebugTraceValue + @" - - "; + $""" + + + {captureDebugTraceValue} + + + """; _testablePlatformServiceProvider.MockSettingsProvider.Setup(sp => sp.Load(It.IsAny())) .Callback((XmlReader actualReader) => @@ -378,10 +382,6 @@ public void TestMethod() [DummyTestClass] private class DummyTestClassWithCleanupMethods { - public DummyTestClassWithCleanupMethods() - { - } - public static Action AssemblyCleanupMethodBody { get; set; } public static Action ClassCleanupMethodBody { get; set; } @@ -400,9 +400,7 @@ public DummyTestClassWithCleanupMethods() public void TestMethod() => TestMethodBody?.Invoke(TestContext); } - private class DummyTestClassAttribute : UTF.TestClassAttribute - { - } + private class DummyTestClassAttribute : UTF.TestClassAttribute; #endregion } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/ExceptionExtensionsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/ExceptionExtensionsTests.cs index 7e59c12db2..b2ac7266a1 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/ExceptionExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/ExceptionExtensionsTests.cs @@ -45,7 +45,6 @@ public void ExceptionTryGetMessageReturnsErrorMessageIfExceptionIsNull() public void ExceptionTryGetMessageShouldThrowIfExceptionMessageThrows() { - string errorMessage = string.Format(CultureInfo.InvariantCulture, Resource.UTF_FailedToGetExceptionMessage, "System.NotImplementedException"); var exception = new DummyException(() => throw new NotImplementedException()); Exception ex = VerifyThrows(() => exception.TryGetMessage()); @@ -56,13 +55,6 @@ public void ExceptionTryGetMessageShouldThrowIfExceptionMessageThrows() #region TryGetStackTraceInformation scenarios - public void TryGetStackTraceInformationReturnsNullIfExceptionIsNull() - { - var exception = (Exception)null; - - Verify(exception.TryGetStackTraceInformation() is null); - } - public void TryGetStackTraceInformationReturnsNullIfExceptionStackTraceIsNullOrEmpty() { var exception = new DummyExceptionForStackTrace(() => null); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/MethodInfoExtensionsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/MethodInfoExtensionsTests.cs index a7da9a45d7..3b7943fd51 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/MethodInfoExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/MethodInfoExtensionsTests.cs @@ -348,8 +348,6 @@ public void MethodInfoInvokeAsSynchronousTaskExecutesAMethodWhichDoesNotReturnAT return true; }; - MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("PublicMethod"); - var dummyTestClass = new DummyTestClass2(); MethodInfo dummyMethod = typeof(DummyTestClass2).GetMethod("DummyMethod"); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestCaseExtensionsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestCaseExtensionsTests.cs index 7743619241..41d0afcda2 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestCaseExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestCaseExtensionsTests.cs @@ -20,7 +20,7 @@ public void ToUnitTestElementShouldReturnUnitTestElementWithFieldsSet() { DisplayName = "DummyDisplayName", }; - string[] testCategories = new[] { "DummyCategory" }; + string[] testCategories = ["DummyCategory"]; testCase.SetPropertyValue(Constants.AsyncTestProperty, true); testCase.SetPropertyValue(Constants.PriorityProperty, 2); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestResultExtensionsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestResultExtensionsTests.cs index e89ba54a80..f8ed0d7d99 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestResultExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestResultExtensionsTests.cs @@ -14,7 +14,7 @@ public class TestResultExtensionsTests : TestContainer { public void ToUnitTestResultsForTestResultWithExceptionConvertsToUnitTestResultsWithFailureOutcome() { - UTF.TestResult[] results = new[] { new UTF.TestResult() { TestFailureException = new Exception() } }; + UTF.TestResult[] results = [new UTF.TestResult() { TestFailureException = new Exception() }]; MSTest.TestAdapter.ObjectModel.UnitTestResult[] convertedResults = results.ToUnitTestResults(); Verify(convertedResults[0].Outcome == AdapterTestOutcome.Failed); @@ -22,7 +22,7 @@ public void ToUnitTestResultsForTestResultWithExceptionConvertsToUnitTestResults public void ToUnitTestResultsForTestResultWithExceptionConvertsToUnitTestResultsWithInconclusiveOutcome() { - UTF.TestResult[] results = new[] { new UTF.TestResult() { TestFailureException = new Exception(), Outcome = UTF.UnitTestOutcome.Inconclusive } }; + UTF.TestResult[] results = [new UTF.TestResult() { TestFailureException = new Exception(), Outcome = UTF.UnitTestOutcome.Inconclusive }]; MSTest.TestAdapter.ObjectModel.UnitTestResult[] convertedResults = results.ToUnitTestResults(); Verify(convertedResults[0].Outcome == AdapterTestOutcome.Inconclusive); @@ -31,8 +31,8 @@ public void ToUnitTestResultsForTestResultWithExceptionConvertsToUnitTestResults public void ToUnitTestResultsForTestResultShouldSetLoggingDataForConvertedUnitTestResults() { var timespan = default(TimeSpan); - UTF.TestResult[] results = new[] - { + UTF.TestResult[] results = + [ new UTF.TestResult() { DebugTrace = "debugTrace", @@ -41,8 +41,8 @@ public void ToUnitTestResultsForTestResultShouldSetLoggingDataForConvertedUnitTe LogOutput = "logOutput", LogError = "logError", DatarowIndex = 1, - }, - }; + } + ]; MSTest.TestAdapter.ObjectModel.UnitTestResult[] convertedResults = results.ToUnitTestResults(); Verify(convertedResults[0].StandardOut == "logOutput"); @@ -55,13 +55,13 @@ public void ToUnitTestResultsForTestResultShouldSetLoggingDataForConvertedUnitTe public void ToUnitTestResultsForTestResultShouldSetStandardOut() { - UTF.TestResult[] results = new[] - { + UTF.TestResult[] results = + [ new UTF.TestResult() { LogOutput = "logOutput", - }, - }; + } + ]; MSTest.TestAdapter.ObjectModel.UnitTestResult[] convertedResults = results.ToUnitTestResults(); Verify(convertedResults[0].StandardOut == "logOutput"); @@ -69,13 +69,13 @@ public void ToUnitTestResultsForTestResultShouldSetStandardOut() public void ToUnitTestResultsForTestResultShouldSetStandardError() { - UTF.TestResult[] results = new[] - { + UTF.TestResult[] results = + [ new UTF.TestResult() { LogError = "logError", - }, - }; + } + ]; MSTest.TestAdapter.ObjectModel.UnitTestResult[] convertedResults = results.ToUnitTestResults(); @@ -84,13 +84,13 @@ public void ToUnitTestResultsForTestResultShouldSetStandardError() public void ToUnitTestResultsForTestResultShouldSetDebugTrace() { - UTF.TestResult[] results = new[] - { + UTF.TestResult[] results = + [ new UTF.TestResult() { DebugTrace = "debugTrace", - }, - }; + } + ]; MSTest.TestAdapter.ObjectModel.UnitTestResult[] convertedResults = results.ToUnitTestResults(); @@ -99,13 +99,13 @@ public void ToUnitTestResultsForTestResultShouldSetDebugTrace() public void ToUnitTestResultsForTestResultShouldSetTestContextMessages() { - UTF.TestResult[] results = new[] - { + UTF.TestResult[] results = + [ new UTF.TestResult() { TestContextMessages = "Context", - }, - }; + } + ]; MSTest.TestAdapter.ObjectModel.UnitTestResult[] convertedResults = results.ToUnitTestResults(); @@ -115,13 +115,13 @@ public void ToUnitTestResultsForTestResultShouldSetTestContextMessages() public void ToUnitTestResultsForTestResultShouldSetDuration() { var timespan = default(TimeSpan); - UTF.TestResult[] results = new[] - { + UTF.TestResult[] results = + [ new UTF.TestResult() { Duration = timespan, - }, - }; + } + ]; MSTest.TestAdapter.ObjectModel.UnitTestResult[] convertedResults = results.ToUnitTestResults(); @@ -130,13 +130,13 @@ public void ToUnitTestResultsForTestResultShouldSetDuration() public void ToUnitTestResultsForTestResultShouldSetDisplayName() { - UTF.TestResult[] results = new[] - { + UTF.TestResult[] results = + [ new UTF.TestResult() { DisplayName = "displayName", - }, - }; + } + ]; MSTest.TestAdapter.ObjectModel.UnitTestResult[] convertedResults = results.ToUnitTestResults(); @@ -145,13 +145,13 @@ public void ToUnitTestResultsForTestResultShouldSetDisplayName() public void ToUnitTestResultsForTestResultShouldSetDataRowIndex() { - UTF.TestResult[] results = new[] - { + UTF.TestResult[] results = + [ new UTF.TestResult() { DatarowIndex = 1, - }, - }; + } + ]; MSTest.TestAdapter.ObjectModel.UnitTestResult[] convertedResults = results.ToUnitTestResults(); @@ -164,15 +164,15 @@ public void ToUnitTestResultsForTestResultShouldSetParentInfo() var parentExecId = Guid.NewGuid(); int innerResultsCount = 5; - UTF.TestResult[] results = new[] - { + UTF.TestResult[] results = + [ new UTF.TestResult() { ExecutionId = executionId, ParentExecId = parentExecId, InnerResultsCount = innerResultsCount, - }, - }; + } + ]; MSTest.TestAdapter.ObjectModel.UnitTestResult[] convertedResults = results.ToUnitTestResults(); @@ -183,7 +183,7 @@ public void ToUnitTestResultsForTestResultShouldSetParentInfo() public void ToUnitTestResultsShouldHaveResultsFileProvidedToTestResult() { - UTF.TestResult[] results = new[] { new UTF.TestResult() { ResultFiles = new List() { "DummyFile.txt" } } }; + UTF.TestResult[] results = [new UTF.TestResult() { ResultFiles = new List() { "DummyFile.txt" } }]; MSTest.TestAdapter.ObjectModel.UnitTestResult[] convertedResults = results.ToUnitTestResults(); Verify(convertedResults[0].ResultFiles[0] == "DummyFile.txt"); } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/DataSerializationHelperTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/DataSerializationHelperTests.cs index d243963033..39109c0c84 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/DataSerializationHelperTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/DataSerializationHelperTests.cs @@ -14,7 +14,7 @@ public void DataSerializerShouldRoundTripDateTimeOffset() var source = new DateTimeOffset(628381323438126060, TimeSpan.FromHours(-8)); object[] actual = DataSerializationHelper.Deserialize( - DataSerializationHelper.Serialize(new object[] { source })); + DataSerializationHelper.Serialize([source])); Verify(actual.Length == 1); Verify(actual[0].GetType() == typeof(DateTimeOffset)); @@ -26,7 +26,7 @@ public void DataSerializerShouldRoundTripDateTime() var source = new DateTime(628381323438126060); object[] actual = DataSerializationHelper.Deserialize( - DataSerializationHelper.Serialize(new object[] { source })); + DataSerializationHelper.Serialize([source])); Verify(actual.Length == 1); Verify(actual[0].GetType() == typeof(DateTime)); @@ -39,7 +39,7 @@ public void DataSerializerShouldRoundTripDateTimeOfKindLocal() var source = new DateTime(628381323438126060, DateTimeKind.Local); object[] actual = DataSerializationHelper.Deserialize( - DataSerializationHelper.Serialize(new object[] { source })); + DataSerializationHelper.Serialize([source])); Verify(actual.Length == 1); Verify(actual[0].GetType() == typeof(DateTime)); @@ -52,7 +52,7 @@ public void DataSerializerShouldRoundTripDateTimeOfKindUtc() var source = new DateTime(628381323438126060, DateTimeKind.Utc); object[] actual = DataSerializationHelper.Deserialize( - DataSerializationHelper.Serialize(new object[] { source })); + DataSerializationHelper.Serialize([source])); Verify(actual.Length == 1); Verify(actual[0].GetType() == typeof(DateTime)); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/ReflectHelperTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/ReflectHelperTests.cs index d39dd6d883..c09f74dbc8 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/ReflectHelperTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/ReflectHelperTests.cs @@ -1,11 +1,13 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Reflection; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -17,18 +19,21 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests; public class ReflectHelperTests : TestContainer { - private readonly TestableReflectHelper _reflectHelper; + private readonly ReflectHelper _reflectHelper; + private readonly AttributeMockingHelper _attributeMockingHelper; private readonly Mock _method; private readonly TestablePlatformServiceProvider _testablePlatformServiceProvider; public ReflectHelperTests() { - _reflectHelper = new TestableReflectHelper(); + _reflectHelper = new(); _method = new Mock(); _method.Setup(x => x.MemberType).Returns(MemberTypes.Method); _testablePlatformServiceProvider = new TestablePlatformServiceProvider(); _testablePlatformServiceProvider.SetupMockReflectionOperations(); + _attributeMockingHelper = new(_testablePlatformServiceProvider.MockReflectionOperations); + PlatformServiceProvider.Instance = _testablePlatformServiceProvider; } @@ -46,10 +51,10 @@ protected override void Dispose(bool disposing) /// public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtClassLevel() { - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("ClassLevel") }, MemberTypes.TypeInfo); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("ClassLevel")], MemberTypes.TypeInfo); string[] expected = ["ClassLevel"]; - string[] actual = _reflectHelper.GetCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); Verify(expected.SequenceEqual(actual)); } @@ -59,12 +64,12 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtClassLevel() /// public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtAllLevels() { - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("AsmLevel1"), new UTF.TestCategoryAttribute("AsmLevel2") }, MemberTypes.All); - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("AsmLevel3") }, MemberTypes.All); - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("ClassLevel") }, MemberTypes.TypeInfo); - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("MethodLevel") }, MemberTypes.Method); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("AsmLevel1"), new UTF.TestCategoryAttribute("AsmLevel2")], MemberTypes.All); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("AsmLevel3")], MemberTypes.All); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("ClassLevel")], MemberTypes.TypeInfo); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("MethodLevel")], MemberTypes.Method); - string[] actual = _reflectHelper.GetCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); string[] expected = ["MethodLevel", "ClassLevel", "AsmLevel1", "AsmLevel2", "AsmLevel3"]; Verify(expected.SequenceEqual(actual)); @@ -75,14 +80,14 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtAllLevels() /// public void GetTestCategoryAttributeShouldConcatCustomAttributeOfSameType() { - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("AsmLevel1") }, MemberTypes.All); - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("AsmLevel2") }, MemberTypes.All); - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("ClassLevel1") }, MemberTypes.TypeInfo); - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("ClassLevel2") }, MemberTypes.TypeInfo); - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("MethodLevel1") }, MemberTypes.Method); - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("MethodLevel2") }, MemberTypes.Method); - - string[] actual = _reflectHelper.GetCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("AsmLevel1")], MemberTypes.All); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("AsmLevel2")], MemberTypes.All); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("ClassLevel1")], MemberTypes.TypeInfo); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("ClassLevel2")], MemberTypes.TypeInfo); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("MethodLevel1")], MemberTypes.Method); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("MethodLevel2")], MemberTypes.Method); + + string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); string[] expected = ["MethodLevel1", "MethodLevel2", "ClassLevel1", "ClassLevel2", "AsmLevel1", "AsmLevel2"]; Verify(expected.SequenceEqual(actual)); @@ -93,11 +98,11 @@ public void GetTestCategoryAttributeShouldConcatCustomAttributeOfSameType() /// public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtAssemblyLevel() { - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("AsmLevel") }, MemberTypes.All); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("AsmLevel")], MemberTypes.All); string[] expected = ["AsmLevel"]; - string[] actual = _reflectHelper.GetCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); Verify(expected.SequenceEqual(actual)); } @@ -107,10 +112,10 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtAssemblyLevel() /// public void GetTestCategoryAttributeShouldIncludeMultipleTestCategoriesAtClassLevel() { - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("ClassLevel"), new UTF.TestCategoryAttribute("ClassLevel1") }, MemberTypes.TypeInfo); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("ClassLevel"), new UTF.TestCategoryAttribute("ClassLevel1")], MemberTypes.TypeInfo); string[] expected = ["ClassLevel", "ClassLevel1"]; - string[] actual = _reflectHelper.GetCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); Verify(expected.SequenceEqual(actual)); } @@ -120,10 +125,10 @@ public void GetTestCategoryAttributeShouldIncludeMultipleTestCategoriesAtClassLe /// public void GetTestCategoryAttributeShouldIncludeMultipleTestCategoriesAtAssemblyLevel() { - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("AsmLevel"), new UTF.TestCategoryAttribute("AsmLevel1") }, MemberTypes.All); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("AsmLevel"), new UTF.TestCategoryAttribute("AsmLevel1")], MemberTypes.All); string[] expected = ["AsmLevel", "AsmLevel1"]; - string[] actual = _reflectHelper.GetCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); Verify(expected.SequenceEqual(actual)); } @@ -132,10 +137,10 @@ public void GetTestCategoryAttributeShouldIncludeMultipleTestCategoriesAtAssembl /// public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtMethodLevel() { - _reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("MethodLevel") }, MemberTypes.Method); + _attributeMockingHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), [new UTF.TestCategoryAttribute("MethodLevel")], MemberTypes.Method); string[] expected = ["MethodLevel"]; - string[] actual = _reflectHelper.GetCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); + string[] actual = _reflectHelper.GetTestCategories(_method.Object, typeof(ReflectHelperTests)).ToArray(); Verify(expected.SequenceEqual(actual)); } @@ -150,7 +155,7 @@ public void IsAttributeDefinedShouldReturnTrueIfSpecifiedAttributeIsDefinedOnAMe Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, true)). Returns(attributes); - Verify(rh.IsAttributeDefined(mockMemberInfo.Object, true)); + Verify(rh.IsNonDerivedAttributeDefined(mockMemberInfo.Object, true)); } public void IsAttributeDefinedShouldReturnFalseIfSpecifiedAttributeIsNotDefinedOnAMember() @@ -163,7 +168,7 @@ public void IsAttributeDefinedShouldReturnFalseIfSpecifiedAttributeIsNotDefinedO Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, true)). Returns(attributes); - Verify(!rh.IsAttributeDefined(mockMemberInfo.Object, true)); + Verify(!rh.IsNonDerivedAttributeDefined(mockMemberInfo.Object, true)); } public void IsAttributeDefinedShouldReturnFromCache() @@ -180,33 +185,16 @@ public void IsAttributeDefinedShouldReturnFromCache() Setup(ro => ro.GetCustomAttributes(memberInfo, true)). Returns(attributes); - Verify(rh.IsAttributeDefined(memberInfo, true)); + Verify(rh.IsNonDerivedAttributeDefined(memberInfo, true)); // Validate that reflection APIs are not called again. - Verify(rh.IsAttributeDefined(memberInfo, true)); + Verify(rh.IsNonDerivedAttributeDefined(memberInfo, true)); _testablePlatformServiceProvider.MockReflectionOperations.Verify(ro => ro.GetCustomAttributes(memberInfo, true), Times.Once); // Also validate that reflection APIs for an individual type is not called since the cache gives us what we need already. _testablePlatformServiceProvider.MockReflectionOperations.Verify(ro => ro.GetCustomAttributes(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } - public void IsAttributeDefinedShouldReturnTrueQueryingASpecificAttributesExistenceEvenIfGettingAllAttributesFail() - { - var rh = new ReflectHelper(); - var mockMemberInfo = new Mock(); - var attributes = new Attribute[] { new UTF.TestMethodAttribute() }; - - _testablePlatformServiceProvider.MockReflectionOperations. - Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, true)). - Returns((object[])null); - - _testablePlatformServiceProvider.MockReflectionOperations. - Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, typeof(UTF.TestMethodAttribute), true)). - Returns(attributes); - - Verify(rh.IsAttributeDefined(mockMemberInfo.Object, true)); - } - public void HasAttributeDerivedFromShouldReturnTrueIfSpecifiedAttributeIsDefinedOnAMember() { var rh = new ReflectHelper(); @@ -217,7 +205,7 @@ public void HasAttributeDerivedFromShouldReturnTrueIfSpecifiedAttributeIsDefined Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, true)). Returns(attributes); - Verify(rh.HasAttributeDerivedFrom(mockMemberInfo.Object, true)); + Verify(rh.IsDerivedAttributeDefined(mockMemberInfo.Object, true)); } public void HasAttributeDerivedFromShouldReturnFalseIfSpecifiedAttributeIsNotDefinedOnAMember() @@ -230,7 +218,7 @@ public void HasAttributeDerivedFromShouldReturnFalseIfSpecifiedAttributeIsNotDef Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, true)). Returns(attributes); - Verify(!rh.IsAttributeDefined(mockMemberInfo.Object, true)); + Verify(!rh.IsNonDerivedAttributeDefined(mockMemberInfo.Object, true)); } public void HasAttributeDerivedFromShouldReturnFromCache() @@ -247,10 +235,10 @@ public void HasAttributeDerivedFromShouldReturnFromCache() Setup(ro => ro.GetCustomAttributes(memberInfo, true)). Returns(attributes); - Verify(rh.HasAttributeDerivedFrom(memberInfo, true)); + Verify(rh.IsDerivedAttributeDefined(memberInfo, true)); // Validate that reflection APIs are not called again. - Verify(rh.HasAttributeDerivedFrom(memberInfo, true)); + Verify(rh.IsDerivedAttributeDefined(memberInfo, true)); _testablePlatformServiceProvider.MockReflectionOperations.Verify(ro => ro.GetCustomAttributes(memberInfo, true), Times.Once); // Also validate that reflection APIs for an individual type is not called since the cache gives us what we need already. @@ -271,31 +259,90 @@ public void HasAttributeDerivedFromShouldReturnFalseQueryingProvidedAttributesEx Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, typeof(UTF.TestMethodAttribute), true)). Returns(attributes); - Verify(!rh.IsAttributeDefined(mockMemberInfo.Object, true)); + Verify(!rh.IsNonDerivedAttributeDefined(mockMemberInfo.Object, true)); } - public void HasAttributeDerivedFromShouldReturnTrueQueryingProvidedAttributesExistenceIfGettingAllAttributesFail() + public void GettingAttributesShouldNotReturnInheritedAttributesWhenAskingForNonInheritedAttributes() { + // This test checks that we get non-inherited attributes when asking for the same type. + // Reflect helper is internally caching the attributes so we don't ask Reflection for them over and over, + // and in the past there was a bug that stored the first ask for the attributes in the cache, not differentiating + // if you asked for inherited, or non-inherited attributes. So if that bug is again put in place you would get 2 attributes + // in both answers. var rh = new ReflectHelper(); var mockMemberInfo = new Mock(); var attributes = new Attribute[] { new TestableExtendedTestMethod() }; _testablePlatformServiceProvider.MockReflectionOperations. - Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, true)). - Returns((object[])null); + Setup(ro => ro.GetCustomAttributes(It.IsAny(), /* inherit */ true)). + Returns([new TestClassAttribute(), new TestClassAttribute()]); _testablePlatformServiceProvider.MockReflectionOperations. - Setup(ro => ro.GetCustomAttributes(mockMemberInfo.Object, typeof(TestableExtendedTestMethod), true)). - Returns(attributes); + Setup(ro => ro.GetCustomAttributes(It.IsAny(), /* inherit */ false)). + Returns([new TestClassAttribute()]); + + var inheritedAttributes = rh.GetDerivedAttributes(typeof(object), inherit: true).ToArray(); + var nonInheritedAttributes = rh.GetDerivedAttributes(typeof(object), inherit: false).ToArray(); - Verify(rh.IsAttributeDefined(mockMemberInfo.Object, true)); + Verify(inheritedAttributes.Length == 2); + Verify(nonInheritedAttributes.Length == 1); + } + + internal class AttributeMockingHelper + { + public AttributeMockingHelper(Mock mockReflectionOperations) + { + _mockReflectionOperations = mockReflectionOperations; + } + + /// + /// A collection to hold mock custom attributes. + /// MemberTypes.All for assembly level + /// MemberTypes.TypeInfo for class level + /// MemberTypes.Method for method level. + /// + private readonly List<(Type Type, Attribute Attribute, MemberTypes MemberType)> _data = []; + private readonly Mock _mockReflectionOperations; + + public void SetCustomAttribute(Type type, Attribute[] values, MemberTypes memberTypes) + { + foreach (Attribute attribute in values) + { + _data.Add((type, attribute, memberTypes)); + } + + _mockReflectionOperations.Setup(r => r.GetCustomAttributes(It.IsAny(), It.IsAny())) + .Returns(GetCustomAttributesNotCached); + _mockReflectionOperations.Setup(r => r.GetCustomAttributes(It.IsAny(), It.IsAny())) + .Returns((assembly, _) => GetCustomAttributesNotCached(assembly, false)); + } + + public object[] GetCustomAttributesNotCached(ICustomAttributeProvider attributeProvider, bool inherit) + { + var foundAttributes = new List(); + foreach ((Type Type, Attribute Attribute, MemberTypes MemberType) attributeData in _data) + { + if (attributeProvider is MethodInfo && (attributeData.MemberType == MemberTypes.Method)) + { + foundAttributes.Add(attributeData.Attribute); + } + else if (attributeProvider is TypeInfo && (attributeData.MemberType == MemberTypes.TypeInfo)) + { + foundAttributes.Add(attributeData.Attribute); + } + else if (attributeProvider is Assembly && attributeData.MemberType == MemberTypes.All) + { + foundAttributes.Add(attributeData.Attribute); + } + } + + return foundAttributes.ToArray(); + } } } #region Dummy Implementations -public class TestableExtendedTestMethod : UTF.TestMethodAttribute -{ -} +public class TestableExtendedTestMethod : UTF.TestMethodAttribute; #endregion diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/RunSettingsUtilitiesTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/RunSettingsUtilitiesTests.cs index f21815f79c..7b5acec0df 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/RunSettingsUtilitiesTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/RunSettingsUtilitiesTests.cs @@ -10,43 +10,45 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Helpers; public class RunSettingsUtilitiesTests : TestContainer { - public void GetTestRunParametersReturnsEmptyDictionaryOnNullRunSettings() + public void GetTestRunParametersReturnsNullOnNullRunSettings() { Dictionary trp = RunSettingsUtilities.GetTestRunParameters(null); - Verify(trp is not null); - Verify(trp.Count == 0); + Verify(trp is null); } - public void GetTestRunParametersReturnsEmptyDictionaryWhenNoTestRunParameters() + public void GetTestRunParametersReturnsNullWhenNoTestRunParameters() { string settingsXml = - @" - - - .\TestResults - x86 - Framework40 - - "; + """ + + + + .\TestResults + x86 + Framework40 + + + """; Dictionary trp = RunSettingsUtilities.GetTestRunParameters(settingsXml); - Verify(trp is not null); - Verify(trp.Count == 0); + Verify(trp is null); } public void GetTestRunParametersReturnsEmptyDictionaryForEmptyTestRunParametersNode() { string settingsXml = - @" - - - .\TestResults - x86 - Framework40 - - - - "; + """ + + + + .\TestResults + x86 + Framework40 + + + + + """; Dictionary trp = RunSettingsUtilities.GetTestRunParameters(settingsXml); Verify(trp is not null); @@ -56,17 +58,19 @@ public void GetTestRunParametersReturnsEmptyDictionaryForEmptyTestRunParametersN public void GetTestRunParametersReturns1EntryOn1TestRunParameter() { string settingsXml = - @" - - - .\TestResults - x86 - Framework40 - - - - - "; + """ + + + + .\TestResults + x86 + Framework40 + + + + + + """; Dictionary trp = RunSettingsUtilities.GetTestRunParameters(settingsXml); Verify(trp is not null); @@ -80,19 +84,21 @@ public void GetTestRunParametersReturns1EntryOn1TestRunParameter() public void GetTestRunParametersReturns3EntryOn3TestRunParameter() { string settingsXml = - @" - - - .\TestResults - x86 - Framework40 - - - - - - - "; + """ + + + + .\TestResults + x86 + Framework40 + + + + + + + + """; Dictionary trp = RunSettingsUtilities.GetTestRunParameters(settingsXml); Verify(trp is not null); @@ -110,17 +116,19 @@ public void GetTestRunParametersReturns3EntryOn3TestRunParameter() public void GetTestRunParametersThrowsWhenTRPNodeHasAttributes() { string settingsXml = - @" - - - .\TestResults - x86 - Framework40 - - - - - "; + """ + + + + .\TestResults + x86 + Framework40 + + + + + + """; Exception ex = VerifyThrows(() => RunSettingsUtilities.GetTestRunParameters(settingsXml)); Verify(ex.GetType() == typeof(SettingsException)); @@ -129,18 +137,20 @@ public void GetTestRunParametersThrowsWhenTRPNodeHasAttributes() public void GetTestRunParametersThrowsWhenTRPNodeHasNonParameterTypeChildNodes() { string settingsXml = - @" - - - .\TestResults - x86 - Framework40 - - - - x86 - - "; + """ + + + + .\TestResults + x86 + Framework40 + + + + x86 + + + """; Exception ex = VerifyThrows(() => RunSettingsUtilities.GetTestRunParameters(settingsXml)); Verify(ex.GetType() == typeof(SettingsException)); @@ -149,17 +159,19 @@ public void GetTestRunParametersThrowsWhenTRPNodeHasNonParameterTypeChildNodes() public void GetTestRunParametersIgnoresMalformedKeyValues() { string settingsXml = - @" - - - .\TestResults - x86 - Framework40 - - - - - "; + """ + + + + .\TestResults + x86 + Framework40 + + + + + + """; Dictionary trp = RunSettingsUtilities.GetTestRunParameters(settingsXml); Verify(trp is not null); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs index 608243725f..14e5441708 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs @@ -17,10 +17,12 @@ public class UnitTestOutcomeHelperTests : TestContainer public UnitTestOutcomeHelperTests() { string runSettingxml = - @" - - - "; + """ + + + + + """; _adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/MSTestDiscovererTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/MSTestDiscovererTests.cs index f1166dde9c..5bc8838140 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/MSTestDiscovererTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/MSTestDiscovererTests.cs @@ -51,35 +51,35 @@ protected override void Dispose(bool disposing) public void MSTestDiscovererHasMSTestAdapterAsExecutorUri() { - DefaultExecutorUriAttribute attribute = typeof(MSTestDiscoverer).GetTypeInfo().GetCustomAttributes(typeof(DefaultExecutorUriAttribute)).Cast().First(); + DefaultExecutorUriAttribute attribute = typeof(MSTestDiscoverer).GetCustomAttributes(typeof(DefaultExecutorUriAttribute)).Cast().First(); Verify(attribute is not null); Verify(attribute.ExecutorUri == "executor://MSTestAdapter/v2"); } public void MSTestDiscovererHasXapAsFileExtension() { - IEnumerable attributes = typeof(MSTestDiscoverer).GetTypeInfo().GetCustomAttributes(typeof(FileExtensionAttribute)).Cast(); + IEnumerable attributes = typeof(MSTestDiscoverer).GetCustomAttributes(typeof(FileExtensionAttribute)).Cast(); Verify(attributes is not null); Verify(attributes.Count(attribute => attribute.FileExtension == ".xap") == 1); } public void MSTestDiscovererHasAppxAsFileExtension() { - IEnumerable attributes = typeof(MSTestDiscoverer).GetTypeInfo().GetCustomAttributes(typeof(FileExtensionAttribute)).Cast(); + IEnumerable attributes = typeof(MSTestDiscoverer).GetCustomAttributes(typeof(FileExtensionAttribute)).Cast(); Verify(attributes is not null); Verify(attributes.Count(attribute => attribute.FileExtension == ".appx") == 1); } public void MSTestDiscovererHasDllAsFileExtension() { - IEnumerable attributes = typeof(MSTestDiscoverer).GetTypeInfo().GetCustomAttributes(typeof(FileExtensionAttribute)).Cast(); + IEnumerable attributes = typeof(MSTestDiscoverer).GetCustomAttributes(typeof(FileExtensionAttribute)).Cast(); Verify(attributes is not null); Verify(attributes.Count(attribute => attribute.FileExtension == ".dll") == 1); } public void MSTestDiscovererHasExeAsFileExtension() { - IEnumerable attributes = typeof(MSTestDiscoverer).GetTypeInfo().GetCustomAttributes(typeof(FileExtensionAttribute)).Cast(); + IEnumerable attributes = typeof(MSTestDiscoverer).GetCustomAttributes(typeof(FileExtensionAttribute)).Cast(); Verify(attributes is not null); Verify(attributes.Count(attribute => attribute.FileExtension == ".exe") == 1); } @@ -161,13 +161,15 @@ public void DiscoverTestsShouldDiscoverTests() public void DiscoveryShouldNotHappenIfTestSettingsIsGiven() { string runSettingsXml = - @" - - DummyPath\\TestSettings1.testsettings - true - true - - "; + """ + + + DummyPath\\TestSettings1.testsettings + true + true + + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); _testablePlatformServiceProvider.MockTestSourceValidator.SetupGet(ts => ts.ValidSourceExtensions).Returns(new List { ".dll" }); @@ -182,13 +184,15 @@ public void DiscoveryShouldNotHappenIfTestSettingsIsGiven() public void DiscoveryShouldReportAndBailOutOnSettingsException() { string runSettingsXml = - @" - - - Pond - - - "; + """ + + + + Pond + + + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); _testablePlatformServiceProvider.MockTestSourceValidator.SetupGet(ts => ts.ValidSourceExtensions).Returns(new List { ".dll" }); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/MSTestExecutorTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/MSTestExecutorTests.cs index f92440940d..5ae5d13da0 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/MSTestExecutorTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/MSTestExecutorTests.cs @@ -44,13 +44,15 @@ public void RunTestsShouldNotExecuteTestsIfTestSettingsIsGiven() var testCase = new TestCase("DummyName", new Uri("executor://MSTestAdapter/v2"), Assembly.GetExecutingAssembly().Location); TestCase[] tests = [testCase]; string runSettingxml = - @" - - DummyPath\\TestSettings1.testsettings - true - true - - "; + """ + + + DummyPath\\TestSettings1.testsettings + true + true + + + """; _mockRunContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); _mstestExecutor.RunTests(tests, _mockRunContext.Object, _mockFrameworkHandle.Object); @@ -64,13 +66,15 @@ public void RunTestsShouldReportErrorAndBailOutOnSettingsException() var testCase = new TestCase("DummyName", new Uri("executor://MSTestAdapter/v2"), Assembly.GetExecutingAssembly().Location); TestCase[] tests = [testCase]; string runSettingxml = - @" - - - Pond - - - "; + """ + + + + Pond + + + + """; _mockRunContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); @@ -86,13 +90,15 @@ public void RunTestsWithSourcesShouldNotExecuteTestsIfTestSettingsIsGiven() { var sources = new List { Assembly.GetExecutingAssembly().Location }; string runSettingxml = - @" - - DummyPath\\TestSettings1.testsettings - true - true - - "; + """ + + + DummyPath\\TestSettings1.testsettings + true + true + + + """; _mockRunContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); _mstestExecutor.RunTests(sources, _mockRunContext.Object, _mockFrameworkHandle.Object); @@ -105,13 +111,15 @@ public void RunTestsWithSourcesShouldReportErrorAndBailOutOnSettingsException() { var sources = new List { Assembly.GetExecutingAssembly().Location }; string runSettingxml = - @" - - - Pond - - - "; + """ + + + + Pond + + + + """; _mockRunContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); @@ -127,8 +135,10 @@ public void RunTestsWithSourcesShouldSetDefaultCollectSourceInformationAsTrue() { var sources = new List { Assembly.GetExecutingAssembly().Location }; string runSettingxml = - @" - "; + """ + + + """; _mockRunContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); _mstestExecutor.RunTests(sources, _mockRunContext.Object, _mockFrameworkHandle.Object); @@ -140,11 +150,13 @@ public void RunTestsWithSourcesShouldSetCollectSourceInformationAsFalseIfSpecifi { var sources = new List { Assembly.GetExecutingAssembly().Location }; string runSettingxml = - @" - - false - - "; + """ + + + false + + + """; _mockRunContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); _mstestExecutor.RunTests(sources, _mockRunContext.Object, _mockFrameworkHandle.Object); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/MSTestSettingsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/MSTestSettingsTests.cs index 0e54ebce7b..66f5e56b64 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/MSTestSettingsTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/MSTestSettingsTests.cs @@ -47,10 +47,12 @@ protected override void Dispose(bool disposing) public void MapInconclusiveToFailedIsByDefaultFalseWhenNotSpecified() { string runSettingxml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -60,10 +62,12 @@ public void MapInconclusiveToFailedIsByDefaultFalseWhenNotSpecified() public void MapNotRunnableToFailedIsByDefaultTrueWhenNotSpecified() { string runSettingxml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -73,11 +77,13 @@ public void MapNotRunnableToFailedIsByDefaultTrueWhenNotSpecified() public void MapInconclusiveToFailedShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - True - - "; + """ + + + True + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -87,11 +93,13 @@ public void MapInconclusiveToFailedShouldBeConsumedFromRunSettingsWhenSpecified( public void MapNotRunnableToFailedShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - True - - "; + """ + + + True + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -101,10 +109,12 @@ public void MapNotRunnableToFailedShouldBeConsumedFromRunSettingsWhenSpecified() public void ForcedLegacyModeIsByDefaultFalseWhenNotSpecified() { string runSettingxml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName); @@ -114,11 +124,13 @@ public void ForcedLegacyModeIsByDefaultFalseWhenNotSpecified() public void ForcedLegacyModeShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - true - - "; + """ + + + true + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName); @@ -128,10 +140,12 @@ public void ForcedLegacyModeShouldBeConsumedFromRunSettingsWhenSpecified() public void TestSettingsFileIsByDefaultNullWhenNotSpecified() { string runSettingxml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName); @@ -141,11 +155,13 @@ public void TestSettingsFileIsByDefaultNullWhenNotSpecified() public void TestSettingsFileShouldNotBeNullWhenSpecifiedInRunSettings() { string runSettingxml = - @" - - DummyPath\\TestSettings1.testsettings - - "; + """ + + + DummyPath\\TestSettings1.testsettings + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName); @@ -155,10 +171,12 @@ public void TestSettingsFileShouldNotBeNullWhenSpecifiedInRunSettings() public void EnableBaseClassTestMethodsFromOtherAssembliesIsByDefaulTrueWhenNotSpecified() { string runSettingxml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -168,11 +186,13 @@ public void EnableBaseClassTestMethodsFromOtherAssembliesIsByDefaulTrueWhenNotSp public void EnableBaseClassTestMethodsFromOtherAssembliesShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - True - - "; + """ + + + True + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -182,10 +202,12 @@ public void EnableBaseClassTestMethodsFromOtherAssembliesShouldBeConsumedFromRun public void CaptureDebugTracesShouldBeTrueByDefault() { string runSettingxml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -195,11 +217,13 @@ public void CaptureDebugTracesShouldBeTrueByDefault() public void CaptureDebugTracesShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - False - - "; + """ + + + False + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -209,11 +233,13 @@ public void CaptureDebugTracesShouldBeConsumedFromRunSettingsWhenSpecified() public void TestTimeoutShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - 4000 - - "; + """ + + + 4000 + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -223,10 +249,12 @@ public void TestTimeoutShouldBeConsumedFromRunSettingsWhenSpecified() public void TestTimeoutShouldBeSetToZeroIfNotSpecifiedInRunSettings() { string runSettingxml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -236,10 +264,12 @@ public void TestTimeoutShouldBeSetToZeroIfNotSpecifiedInRunSettings() public void TreatClassCleanupWarningsAsErrorsShouldBeFalseByDefault() { string runSettingxml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -249,11 +279,13 @@ public void TreatClassCleanupWarningsAsErrorsShouldBeFalseByDefault() public void TreatClassCleanupWarningsAsErrorsShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - True - - "; + """ + + + True + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -263,10 +295,12 @@ public void TreatClassCleanupWarningsAsErrorsShouldBeConsumedFromRunSettingsWhen public void TreatDiscoveryWarningsAsErrorsShouldBeFalseByDefault() { string runSettingxml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -276,11 +310,13 @@ public void TreatDiscoveryWarningsAsErrorsShouldBeFalseByDefault() public void TreatDiscoveryWarningsAsErrorsShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - True - - "; + """ + + + True + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -290,10 +326,12 @@ public void TreatDiscoveryWarningsAsErrorsShouldBeConsumedFromRunSettingsWhenSpe public void ParallelizationSettingsShouldNotBeSetByDefault() { string runSettingxml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -304,13 +342,15 @@ public void ParallelizationSettingsShouldNotBeSetByDefault() public void GetSettingsShouldThrowIfParallelizationWorkersIsNotInt() { string runSettingxml = - @" - - - GoneFishing - - - "; + """ + + + + GoneFishing + + + + """; Exception exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias)); @@ -322,13 +362,15 @@ public void GetSettingsShouldThrowIfParallelizationWorkersIsNotInt() public void GetSettingsShouldThrowIfParallelizationWorkersIsNegative() { string runSettingxml = - @" - - - -1 - - - "; + """ + + + + -1 + + + + """; Exception exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias)); @@ -340,13 +382,15 @@ public void GetSettingsShouldThrowIfParallelizationWorkersIsNegative() public void ParallelizationWorkersShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - - 2 - - - "; + """ + + + + 2 + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -356,13 +400,15 @@ public void ParallelizationWorkersShouldBeConsumedFromRunSettingsWhenSpecified() public void ParallelizationWorkersShouldBeSetToProcessorCountWhenSetToZero() { string runSettingxml = - @" - - - 0 - - - "; + """ + + + + 0 + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -372,12 +418,14 @@ public void ParallelizationWorkersShouldBeSetToProcessorCountWhenSetToZero() public void ParallelizationSettingsShouldBeSetToDefaultsWhenNotSet() { string runSettingxml = - @" - - - - - "; + """ + + + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -388,11 +436,13 @@ public void ParallelizationSettingsShouldBeSetToDefaultsWhenNotSet() public void ParallelizationSettingsShouldBeSetToDefaultsOnAnEmptyParalleizeSetting() { string runSettingxml = - @" - - - - "; + """ + + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -403,14 +453,16 @@ public void ParallelizationSettingsShouldBeSetToDefaultsOnAnEmptyParalleizeSetti public void ParallelizationSettingsShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - - 127 - MethodLevel - - - "; + """ + + + + 127 + MethodLevel + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -421,13 +473,15 @@ public void ParallelizationSettingsShouldBeConsumedFromRunSettingsWhenSpecified( public void GetSettingsShouldThrowIfParallelizationScopeIsNotValid() { string runSettingxml = - @" - - - JustParallelizeWillYou - - - "; + """ + + + + JustParallelizeWillYou + + + + """; Exception exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias)); @@ -439,13 +493,15 @@ public void GetSettingsShouldThrowIfParallelizationScopeIsNotValid() public void ParallelizationScopeShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - - MethodLevel - - - "; + """ + + + + MethodLevel + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -455,13 +511,15 @@ public void ParallelizationScopeShouldBeConsumedFromRunSettingsWhenSpecified() public void GetSettingsShouldThrowWhenParallelizeHasInvalidElements() { string runSettingxml = - @" - - - Hi - - - "; + """ + + + + Hi + + + + """; Exception exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias)); @@ -473,13 +531,15 @@ public void GetSettingsShouldThrowWhenParallelizeHasInvalidElements() public void GetSettingsShouldBeAbleToReadAfterParallelizationSettings() { string runSettingxml = - @" - - - - DummyPath\\TestSettings1.testsettings - - "; + """ + + + + + DummyPath\\TestSettings1.testsettings + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -489,15 +549,17 @@ public void GetSettingsShouldBeAbleToReadAfterParallelizationSettings() public void GetSettingsShouldBeAbleToReadAfterParallelizationSettingsWithData() { string runSettingxml = - @" - - - 127 - MethodLevel - - DummyPath\\TestSettings1.testsettings - - "; + """ + + + + 127 + MethodLevel + + DummyPath\\TestSettings1.testsettings + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -509,12 +571,14 @@ public void GetSettingsShouldBeAbleToReadAfterParallelizationSettingsWithData() public void GetSettingsShouldBeAbleToReadAfterParallelizationSettingsOnEmptyParallelizationNode() { string runSettingxml = - @" - - - DummyPath\\TestSettings1.testsettings - - "; + """ + + + + DummyPath\\TestSettings1.testsettings + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias); @@ -524,8 +588,10 @@ public void GetSettingsShouldBeAbleToReadAfterParallelizationSettingsOnEmptyPara public void DisableParallelizationShouldBeFalseByDefault() { string runSettingxml = - @" - "; + """ + + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); @@ -537,11 +603,13 @@ public void DisableParallelizationShouldBeFalseByDefault() public void DisableParallelizationShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - True - - "; + """ + + + True + + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); @@ -557,11 +625,13 @@ public void DisableParallelizationShouldBeConsumedFromRunSettingsWhenSpecified() public void GetSettingsShouldProbePlatformSpecificSettingsAlso() { string runSettingxml = - @" - - True - - "; + """ + + + True + + + """; _testablePlatformServiceProvider.MockSettingsProvider.Setup(sp => sp.Load(It.IsAny())) .Callback((XmlReader actualReader) => @@ -580,12 +650,14 @@ public void GetSettingsShouldProbePlatformSpecificSettingsAlso() public void GetSettingsShouldOnlyPassTheElementSubTreeToPlatformService() { string runSettingxml = - @" - - True - True - - "; + """ + + + True + True + + + """; string expectedrunSettingxml = "True"; string observedxml = null; @@ -607,14 +679,16 @@ public void GetSettingsShouldOnlyPassTheElementSubTreeToPlatformService() public void GetSettingsShouldBeAbleToReadSettingsAfterThePlatformServiceReadsItsSettings() { string runSettingxml = - @" - - True - True - True - DummyPath\\TestSettings1.testsettings - - "; + """ + + + True + True + True + DummyPath\\TestSettings1.testsettings + + + """; bool dummyPlatformSpecificSetting = false; @@ -660,17 +734,19 @@ public void GetSettingsShouldBeAbleToReadSettingsAfterThePlatformServiceReadsIts public void GetSettingsShouldBeAbleToReadSettingsIfThePlatformServiceDoesNotUnderstandASetting() { string runSettingxml = - @" - - True - True - foobar - true - true - True - DummyPath\\TestSettings1.testsettings - - "; + """ + + + True + True + foobar + true + true + True + DummyPath\\TestSettings1.testsettings + + + """; bool dummyPlatformSpecificSetting = false; @@ -718,13 +794,15 @@ public void GetSettingsShouldBeAbleToReadSettingsIfThePlatformServiceDoesNotUnde public void GetSettingsShouldOnlyReadTheAdapterSection() { string runSettingxml = - @" - - True - True - - Bad - "; + """ + + + True + True + + Bad + + """; bool outOfScopeCall = false; @@ -765,20 +843,22 @@ public void GetSettingsShouldOnlyReadTheAdapterSection() public void GetSettingsShouldWorkIfThereAreCommentsInTheXML() { string runSettingxml = - @" - - - - True - True - True - - true - - true - - Bad - "; + """ + + + + + True + True + True + + true + + true + + Bad + + """; bool dummyPlatformSpecificSetting = false; @@ -840,11 +920,13 @@ public void CurrentSettingShouldReturnDefaultSettingsIfNotSet() public void CurrentSettingShouldReturnCachedLoadedSettings() { string runSettingxml = - @" - - DummyPath\\TestSettings1.testsettings - - "; + """ + + + DummyPath\\TestSettings1.testsettings + + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); @@ -866,17 +948,19 @@ public void CurrentSettingShouldReturnCachedLoadedSettings() public void PopulateSettingsShouldFillInSettingsFromSettingsObject() { string runsettingsXml = - @" - - False - True - True - DummyPath\\TestSettings1.testsettings - true - true - true - - "; + """ + + + False + True + True + DummyPath\\TestSettings1.testsettings + true + true + true + + + """; var settings = MSTestSettings.GetSettings(runsettingsXml, MSTestSettings.SettingsName); @@ -931,11 +1015,13 @@ public void PopulateSettingsShouldInitializeDefaultSettingsWhenRunSettingsXmlIsE public void PopulateSettingsShouldInitializeSettingsToDefaultIfNotSpecified() { string runSettingxml = - @" - - DummyPath\\TestSettings1.testsettings - - "; + """ + + + DummyPath\\TestSettings1.testsettings + + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); @@ -952,15 +1038,17 @@ public void PopulateSettingsShouldInitializeSettingsToDefaultIfNotSpecified() public void PopulateSettingsShouldInitializeSettingsFromMSTestSection() { string runSettingxml = - @" - - True - True - DummyPath\\TestSettings1.testsettings - true - true - - "; + """ + + + True + True + DummyPath\\TestSettings1.testsettings + true + true + + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); @@ -980,15 +1068,17 @@ public void PopulateSettingsShouldInitializeSettingsFromMSTestSection() public void PopulateSettingsShouldInitializeSettingsFromMSTestV2Section() { string runSettingxml = - @" - - True - True - DummyPath\\TestSettings1.testsettings - true - true - - "; + """ + + + True + True + DummyPath\\TestSettings1.testsettings + true + true + + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); @@ -1008,18 +1098,20 @@ public void PopulateSettingsShouldInitializeSettingsFromMSTestV2Section() public void PopulateSettingsShouldInitializeSettingsFromMSTestV2OverMSTestV1Section() { string runSettingxml = - @" - - True - True - true - - - False - DummyPath\\TestSettings1.testsettings - true - - "; + """ + + + True + True + true + + + False + DummyPath\\TestSettings1.testsettings + true + + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); @@ -1050,11 +1142,13 @@ public void IsLegacyScenarioReturnsFalseWhenDiscoveryContextIsNull() public void IsLegacyScenarioReturnsFalseWhenForcedLegacyModeIsSetToFalse() { string runSettingxml = - @" - - False + """ + + + False - "; + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); @@ -1065,11 +1159,13 @@ public void IsLegacyScenarioReturnsFalseWhenForcedLegacyModeIsSetToFalse() public void IsLegacyScenarioReturnsFalseWhenForcedLegacyModeIsSetToTrue() { string runSettingxml = - @" - - true + """ + + + true - "; + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object); @@ -1079,11 +1175,13 @@ public void IsLegacyScenarioReturnsFalseWhenForcedLegacyModeIsSetToTrue() public void IsLegacyScenarioReturnsTrueWhenTestSettingsFileIsGiven() { string runSettingxml = - @" - - DummyPath\\TestSettings1.testsettings + """ + + + DummyPath\\TestSettings1.testsettings - "; + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object); @@ -1093,11 +1191,13 @@ public void IsLegacyScenarioReturnsTrueWhenTestSettingsFileIsGiven() public void LegacyScenariosNotSupportedWarningIsPrintedWhenVsmdiFileIsGiven() { string runSettingxml = - @" - - DummyPath\\vsmdiFile.vsmdi + """ + + + DummyPath\\vsmdiFile.vsmdi - "; + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestElementTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestElementTests.cs index a74801c9cf..09afbda027 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestElementTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestElementTests.cs @@ -110,12 +110,12 @@ public void ToTestCaseShouldSetTestCategoryIfPresent() Verify(testCase.GetPropertyValue(Constants.TestCategoryProperty) is null); - _unitTestElement.TestCategory = Array.Empty(); + _unitTestElement.TestCategory = []; testCase = _unitTestElement.ToTestCase(); Verify(testCase.GetPropertyValue(Constants.TestCategoryProperty) is null); - _unitTestElement.TestCategory = new string[] { "TC" }; + _unitTestElement.TestCategory = ["TC"]; testCase = _unitTestElement.ToTestCase(); Verify(new string[] { "TC" }.SequenceEqual((string[])testCase.GetPropertyValue(Constants.TestCategoryProperty))); @@ -144,7 +144,7 @@ public void ToTestCaseShouldSetTraitsIfPresent() #pragma warning restore CA1827 // Do not use Count() or LongCount() when Any() can be used var trait = new TestPlatform.ObjectModel.Trait("trait", "value"); - _unitTestElement.Traits = new TestPlatform.ObjectModel.Trait[] { trait }; + _unitTestElement.Traits = [trait]; testCase = _unitTestElement.ToTestCase(); Verify(testCase.Traits.Count() == 1); @@ -157,7 +157,7 @@ public void ToTestCaseShouldSetPropertiesIfPresent() _unitTestElement.CssIteration = "12"; _unitTestElement.CssProjectStructure = "ProjectStructure"; _unitTestElement.Description = "I am a dummy test"; - _unitTestElement.WorkItemIds = new string[] { "2312", "22332" }; + _unitTestElement.WorkItemIds = ["2312", "22332"]; var testCase = _unitTestElement.ToTestCase(); @@ -174,12 +174,12 @@ public void ToTestCaseShouldSetDeploymentItemPropertyIfPresent() Verify(testCase.GetPropertyValue(Constants.DeploymentItemsProperty) is null); - _unitTestElement.DeploymentItems = Array.Empty>(); + _unitTestElement.DeploymentItems = []; testCase = _unitTestElement.ToTestCase(); Verify(testCase.GetPropertyValue(Constants.DeploymentItemsProperty) is null); - _unitTestElement.DeploymentItems = new KeyValuePair[] { new("s", "d") }; + _unitTestElement.DeploymentItems = [new("s", "d")]; testCase = _unitTestElement.ToTestCase(); Verify(_unitTestElement.DeploymentItems.SequenceEqual(testCase.GetPropertyValue(Constants.DeploymentItemsProperty) as KeyValuePair[])); @@ -232,8 +232,8 @@ public void ToTestCase_WhenStrategyIsData_DoesNotUseDefaultTestCaseId() public void ToTestCase_WhenStrategyIsDisplayName_ExamplesOfTestCaseIdUniqueness() { TestIdGenerationStrategy testIdStrategy = TestIdGenerationStrategy.DisplayName; - TestCase[] testCases = new[] - { + TestCase[] testCases = + [ new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", false, testIdStrategy)) .ToTestCase(), @@ -263,8 +263,8 @@ public void ToTestCase_WhenStrategyIsDisplayName_ExamplesOfTestCaseIdUniqueness( { DisplayName = "SomeOtherDisplayName", } - .ToTestCase(), - }; + .ToTestCase() + ]; Verify(testCases.Select(tc => tc.Id.ToString()).Distinct().Count() == testCases.Length); } @@ -273,8 +273,8 @@ public void ToTestCase_WhenStrategyIsDisplayName_ExamplesOfTestCaseIdUniqueness( public void ToTestCase_WhenStrategyIsDisplayName_ExamplesOfTestCaseIdCollision() { TestIdGenerationStrategy testIdStrategy = TestIdGenerationStrategy.DisplayName; - TestCase[] testCases = new[] - { + TestCase[] testCases = + [ new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", false, testIdStrategy) { @@ -285,31 +285,31 @@ public void ToTestCase_WhenStrategyIsDisplayName_ExamplesOfTestCaseIdCollision() new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", false, testIdStrategy) { DataType = DynamicDataType.DataSourceAttribute, - SerializedData = new[] { "1", }, + SerializedData = ["1"], }) .ToTestCase(), new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", false, testIdStrategy) { DataType = DynamicDataType.DataSourceAttribute, - SerializedData = new[] { "2", }, + SerializedData = ["2"], }) .ToTestCase(), new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", false, testIdStrategy) { DataType = DynamicDataType.ITestDataSource, - SerializedData = new[] { "1", }, + SerializedData = ["1"], }) .ToTestCase(), new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", false, testIdStrategy) { DataType = DynamicDataType.ITestDataSource, - SerializedData = new[] { "2", }, + SerializedData = ["2"], }) - .ToTestCase(), - }; + .ToTestCase() + ]; Verify(testCases.Select(tc => tc.Id.ToString()).Distinct().Count() == 1); } @@ -317,8 +317,8 @@ public void ToTestCase_WhenStrategyIsDisplayName_ExamplesOfTestCaseIdCollision() public void ToTestCase_WhenStrategyIsFullyQualifiedTest_ExamplesOfTestCaseIdUniqueness() { TestIdGenerationStrategy testIdStrategy = TestIdGenerationStrategy.FullyQualified; - TestCase[] testCases = new[] - { + TestCase[] testCases = + [ new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", false, testIdStrategy)) .ToTestCase(), @@ -334,22 +334,22 @@ public void ToTestCase_WhenStrategyIsFullyQualifiedTest_ExamplesOfTestCaseIdUniq new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", false, testIdStrategy) { - SerializedData = new[] { "System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "[]", }, + SerializedData = ["System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "[]"], }) .ToTestCase(), new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", false, testIdStrategy) { - SerializedData = new[] { "System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "[1]", }, + SerializedData = ["System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "[1]"], }) .ToTestCase(), new UnitTestElement( new("MyMethod", "MyProduct.MyNamespace.MyClass", "MyAssembly", false, testIdStrategy) { - SerializedData = new[] { "System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "[1,1]", }, + SerializedData = ["System.Int32[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "[1,1]"], }) - .ToTestCase(), - }; + .ToTestCase() + ]; Verify(testCases.Select(tc => tc.Id.ToString()).Distinct().Count() == testCases.Length); } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestResultTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestResultTests.cs index d8852bb32a..510f58c29e 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestResultTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestResultTests.cs @@ -55,10 +55,12 @@ public void ToTestResultShouldReturnConvertedTestResultWithFieldsSet() DateTimeOffset endTime = DateTimeOffset.Now; string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -86,10 +88,12 @@ public void ToTestResultForUniTestResultWithStandardOutShouldReturnTestResultWit TestCase testCase = new("Foo", new Uri("Uri", UriKind.Relative), Assembly.GetExecutingAssembly().FullName); string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -106,10 +110,12 @@ public void ToTestResultForUniTestResultWithStandardErrorShouldReturnTestResultW TestCase testCase = new("Foo", new Uri("Uri", UriKind.Relative), Assembly.GetExecutingAssembly().FullName); string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -126,10 +132,12 @@ public void ToTestResultForUniTestResultWithDebugTraceShouldReturnTestResultWith TestCase testCase = new("Foo", new Uri("Uri", UriKind.Relative), Assembly.GetExecutingAssembly().FullName); string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -146,10 +154,12 @@ public void ToTestResultForUniTestResultWithTestContextMessagesShouldReturnTestR TestCase testCase = new("Foo", new Uri("Uri", UriKind.Relative), Assembly.GetExecutingAssembly().FullName); string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -166,10 +176,12 @@ public void ToTestResultForUniTestResultWithResultFilesShouldReturnTestResultWit TestCase testCase = new("Foo", new Uri("Uri", UriKind.Relative), Assembly.GetExecutingAssembly().FullName); string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -188,10 +200,12 @@ public void ToTestResultForUniTestResultWithNoResultFilesShouldReturnTestResultW TestCase testCase = new("Foo", new Uri("Uri", UriKind.Relative), Assembly.GetExecutingAssembly().FullName); string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -215,10 +229,12 @@ public void ToTestResultForUniTestResultWithParentInfoShouldReturnTestResultWith TestCase testCase = new("Foo", new Uri("Uri", UriKind.Relative), Assembly.GetExecutingAssembly().FullName); string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -232,10 +248,12 @@ public void ToTestResultForUniTestResultWithParentInfoShouldReturnTestResultWith public void UniTestHelperToTestOutcomeForUnitTestOutcomePassedShouldReturnTestOutcomePassed() { string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -246,10 +264,12 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomePassedShouldReturnTestOu public void UniTestHelperToTestOutcomeForUnitTestOutcomeFailedShouldReturnTestOutcomeFailed() { string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -260,10 +280,12 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeFailedShouldReturnTestOu public void UniTestHelperToTestOutcomeForUnitTestOutcomeErrorShouldReturnTestOutcomeFailed() { string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -274,11 +296,13 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeErrorShouldReturnTestOut public void UnitTestHelperToTestOutcomeForUnitTestOutcomeNotRunnableShouldReturnTestOutComeNoneWhenSpecifiedInAdapterSettings() { string runSettingsXml = - @" - - false - - "; + """ + + + false + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -289,10 +313,12 @@ public void UnitTestHelperToTestOutcomeForUnitTestOutcomeNotRunnableShouldReturn public void UnitTestHelperToTestOutcomeForUnitTestOutcomeNotRunnableShouldReturnTestOutcomeFailedByDefault() { string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -303,10 +329,12 @@ public void UnitTestHelperToTestOutcomeForUnitTestOutcomeNotRunnableShouldReturn public void UniTestHelperToTestOutcomeForUnitTestOutcomeTimeoutShouldReturnTestOutcomeFailed() { string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -317,10 +345,12 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeTimeoutShouldReturnTestO public void UniTestHelperToTestOutcomeForUnitTestOutcomeIgnoredShouldReturnTestOutcomeSkipped() { string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -331,10 +361,12 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeIgnoredShouldReturnTestO public void UniTestHelperToTestOutcomeForUnitTestOutcomeInconclusiveShouldReturnTestOutcomeSkipped() { string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -345,11 +377,13 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeInconclusiveShouldReturn public void UniTestHelperToTestOutcomeForUnitTestOutcomeInconclusiveShouldReturnTestOutcomeFailedWhenSpecifiedSo() { string runSettingsXml = - @" - - true - - "; + """ + + + true + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -360,10 +394,12 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeInconclusiveShouldReturn public void UniTestHelperToTestOutcomeForUnitTestOutcomeNotFoundShouldReturnTestOutcomeNotFound() { string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -374,10 +410,12 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeNotFoundShouldReturnTest public void UniTestHelperToTestOutcomeForUnitTestOutcomeInProgressShouldReturnTestOutcomeNone() { string runSettingsXml = - @" - - - "; + """ + + + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); @@ -388,11 +426,13 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeInProgressShouldReturnTe public void UniTestHelperToTestOutcomeForUnitTestOutcomeNotRunnableShouldReturnTestOutcomeFailedWhenSpecifiedSo() { string runSettingsXml = - @" - - true - - "; + """ + + + true + + + """; var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias); var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.NotRunnable, adapterSettings); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/RunConfigurationSettingsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/RunConfigurationSettingsTests.cs index 919bc256c9..cef40d425e 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/RunConfigurationSettingsTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/RunConfigurationSettingsTests.cs @@ -38,10 +38,12 @@ protected override void Dispose(bool disposing) public void CollectSourceInformationIsByDefaultTrueWhenNotSpecified() { string runSettingxml = - @" - - - "; + """ + + + + + """; var configurationSettings = RunConfigurationSettings.GetSettings(runSettingxml, RunConfigurationSettings.SettingsName); Verify(configurationSettings.CollectSourceInformation); @@ -50,13 +52,15 @@ public void CollectSourceInformationIsByDefaultTrueWhenNotSpecified() public void CollectSourceInformationShouldBeConsumedFromRunSettingsWhenSpecified() { string runSettingxml = - @" - - - .\TestResults - false - - "; + """ + + + + .\TestResults + false + + + """; var configurationSettings = RunConfigurationSettings.GetSettings(runSettingxml, RunConfigurationSettings.SettingsName); Verify(!configurationSettings.CollectSourceInformation); @@ -109,11 +113,13 @@ public void PopulateSettingsShouldInitializeDefaultSettingsWhenRunSettingsXmlIsE public void PopulateSettingsShouldInitializeSettingsToDefaultIfNotSpecified() { string runSettingxml = - @" - - DummyPath\\TestSettings1.testsettings - - "; + """ + + + DummyPath\\TestSettings1.testsettings + + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); @@ -129,12 +135,14 @@ public void PopulateSettingsShouldInitializeSettingsToDefaultIfNotSpecified() public void PopulateSettingsShouldInitializeSettingsFromRunConfigurationSection() { string runSettingxml = - @" - - .\TestResults - false - - "; + """ + + + .\TestResults + false + + + """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/TestableImplementations/TestableReflectHelper.cs b/test/UnitTests/MSTestAdapter.UnitTests/TestableImplementations/TestableReflectHelper.cs deleted file mode 100644 index 42811bd3de..0000000000 --- a/test/UnitTests/MSTestAdapter.UnitTests/TestableImplementations/TestableReflectHelper.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Reflection; - -using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; - -namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; - -/// -/// A testable implementation of reflect helper. -/// -internal sealed class TestableReflectHelper : ReflectHelper -{ - /// - /// A dictionary to hold mock custom attributes. The int represents a hash code of - /// the Type of custom attribute and the level its applied at : - /// MemberTypes.All for assembly level - /// MemberTypes.TypeInfo for class level - /// MemberTypes.Method for method level. - /// - private readonly Dictionary _customAttributes; - - public TestableReflectHelper() - { - _customAttributes = []; - } - - public void SetCustomAttribute(Type type, Attribute[] values, MemberTypes memberTypes) - { - int hashCode = type.FullName.GetHashCode() + memberTypes.GetHashCode(); - _customAttributes[hashCode] = _customAttributes.TryGetValue(hashCode, out Attribute[] value) - ? value.Concat(values).ToArray() - : values; - } - - internal override TAttribute[] GetCustomAttributeForAssembly(MemberInfo memberInfo) - { - int hashCode = MemberTypes.All.GetHashCode() + typeof(TAttribute).FullName.GetHashCode(); - - return _customAttributes.TryGetValue(hashCode, out Attribute[] value) - ? value.OfType().ToArray() - : Array.Empty(); - } - - internal override TAttribute[] GetCustomAttributes(MemberInfo memberInfo) - { - int hashCode = memberInfo.MemberType.GetHashCode() + typeof(TAttribute).FullName.GetHashCode(); - - return _customAttributes.TryGetValue(hashCode, out Attribute[] value) - ? value.OfType().ToArray() - : Array.Empty(); - } -} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AppInsightsProviderTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AppInsightsProviderTests.cs new file mode 100644 index 0000000000..2d025fb0ae --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AppInsightsProviderTests.cs @@ -0,0 +1,159 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.Telemetry; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Services; +using Microsoft.Testing.Platform.Telemetry; + +using Moq; + +namespace Microsoft.Testing.Extensions.UnitTests; + +[TestGroup] +public sealed class AppInsightsProviderTests : TestBase +{ + public AppInsightsProviderTests(ITestExecutionContext testExecutionContext) + : base(testExecutionContext) + { + } + + public void Platform_CancellationToken_Cancellation_Should_Exit_Gracefully() + { + Mock environment = new(); + Mock clock = new(); + Mock config = new(); + Mock telemetryInformation = new(); + + Mock loggerFactory = new(); + loggerFactory.Setup(x => x.CreateLogger(It.IsAny())).Returns(new Mock().Object); + + ManualResetEvent loopInitialized = new(false); + ManualResetEvent sample2Message = new(false); + CancellationTokenSource cancellationTokenSource = new(); + Mock testApplicationCancellationTokenSource = new(); + testApplicationCancellationTokenSource.Setup(x => x.CancellationToken).Returns(cancellationTokenSource.Token); + + List events = new(); + Mock testTelemetryClient = new(); + testTelemetryClient.Setup(x => x.TrackEvent(It.IsAny(), It.IsAny>(), It.IsAny>())) + .Callback((string eventName, Dictionary properties, Dictionary metrics) => + { + loopInitialized.Set(); + events.Add(eventName); + sample2Message.WaitOne(); + }); + + Mock telemetryClientFactory = new(); + telemetryClientFactory.Setup(x => x.Create(It.IsAny(), It.IsAny())).Returns(testTelemetryClient.Object); + + AppInsightsProvider appInsightsProvider = new( + environment.Object, + testApplicationCancellationTokenSource.Object, + new SystemTask(), + loggerFactory.Object, + clock.Object, + config.Object, + telemetryInformation.Object, + telemetryClientFactory.Object, + "sessionId"); + + // Fire the consume loop + _ = appInsightsProvider.LogEventAsync("Sample", new Dictionary()); + + // Wait for the consume loop + loopInitialized.WaitOne(); + + // Fire the consume loop + _ = appInsightsProvider.LogEventAsync("Sample2", new Dictionary()); + + // Cancel the platform token + cancellationTokenSource.Cancel(); + + sample2Message.Set(); + +#if NETCOREAPP + ValueTask valueTask = appInsightsProvider.DisposeAsync(); + while (!valueTask.IsCompleted) + { + } +#else + appInsightsProvider.Dispose(); +#endif + + // We expect to not consume the second event because we exit the inner loop for the cancellation token + Assert.IsTrue(events.Single() == "Sample"); + } + + public void Timeout_During_Dispose_Should_Exit_Gracefully() + { + Mock environment = new(); + Mock clock = new(); + Mock config = new(); + Mock telemetryInformation = new(); + + Mock loggerFactory = new(); + loggerFactory.Setup(x => x.CreateLogger(It.IsAny())).Returns(new Mock().Object); + + ManualResetEvent loopInitialized = new(false); + CancellationTokenSource cancellationTokenSource = new(); + Mock testApplicationCancellationTokenSource = new(); + testApplicationCancellationTokenSource.Setup(x => x.CancellationToken).Returns(cancellationTokenSource.Token); + + int calls = 0; + Mock testTelemetryClient = new(); + testTelemetryClient.Setup(x => x.TrackEvent(It.IsAny(), It.IsAny>(), It.IsAny>())) + .Callback((string eventName, Dictionary properties, Dictionary metrics) => + { + if (calls == 0) + { + loopInitialized.Set(); + calls++; + return; + } + + if (calls == 1) + { + // Timeout for more than 3 seconds + Thread.Sleep(10_000); + return; + } + }); + + Mock telemetryClientFactory = new(); + telemetryClientFactory.Setup(x => x.Create(It.IsAny(), It.IsAny())).Returns(testTelemetryClient.Object); + + AppInsightsProvider appInsightsProvider = new( + environment.Object, + testApplicationCancellationTokenSource.Object, + new SystemTask(), + loggerFactory.Object, + clock.Object, + config.Object, + telemetryInformation.Object, + telemetryClientFactory.Object, + "sessionId"); + + // Fire the consume loop + _ = appInsightsProvider.LogEventAsync("Sample", new Dictionary()); + + // Wait for the consume loop + loopInitialized.WaitOne(); + + // Dispose the application token + cancellationTokenSource.Dispose(); + + // Fire the second loop that will timeout + Task logTask = appInsightsProvider.LogEventAsync("Sample", new Dictionary()); +#if NETCOREAPP + ValueTask valueTask = appInsightsProvider.DisposeAsync(); + while (!valueTask.IsCompleted) + { + } +#else + appInsightsProvider.Dispose(); +#endif + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/CrashDumpTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/CrashDumpTests.cs new file mode 100644 index 0000000000..a858eccdf2 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/CrashDumpTests.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.Diagnostics; +using Microsoft.Testing.Extensions.Diagnostics.Resources; +using Microsoft.Testing.Extensions.UnitTests.Helpers; +using Microsoft.Testing.Platform.Extensions.CommandLine; + +namespace Microsoft.Testing.Extensions.UnitTests; + +[TestGroup] +public class CrashDumpTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + [Arguments("Mini")] + [Arguments("Heap")] + [Arguments("Triage")] + [Arguments("Full")] + public async Task IsValid_If_CrashDumpType_Has_CorrectValue(string crashDumpType) + { + var provider = new CrashDumpCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == CrashDumpCommandLineOptions.CrashDumpTypeOptionName); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [crashDumpType]).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + public async Task IsInvValid_If_CrashDumpType_Has_IncorrectValue() + { + var provider = new CrashDumpCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == CrashDumpCommandLineOptions.CrashDumpTypeOptionName); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, ["invalid"]).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CrashDumpTypeOptionInvalidType, "invalid"), validateOptionsResult.ErrorMessage); + } + + public async Task CrashDump_CommandLineOptions_Are_AlwaysValid() + { + var provider = new CrashDumpCommandLineProvider(); + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions([])).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HangDumpTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HangDumpTests.cs new file mode 100644 index 0000000000..c3c239e37f --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HangDumpTests.cs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.Diagnostics; +using Microsoft.Testing.Extensions.Diagnostics.Resources; +using Microsoft.Testing.Extensions.UnitTests.Helpers; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.IPC; +using Microsoft.Testing.Platform.Services; + +using Moq; + +namespace Microsoft.Testing.Extensions.UnitTests; + +[TestGroup] +public class HangDumpTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + private HangDumpCommandLineProvider GetProvider() + { + var testApplicationModuleInfo = new Mock(); + _ = testApplicationModuleInfo.Setup(x => x.GetCurrentTestApplicationFullPath()).Returns("FullPath"); + return new(new HangDumpConfiguration( + testApplicationModuleInfo.Object, + new PipeNameDescription("pipeName", false), + "suffix")); + } + + public async Task IsValid_If_Timeout_Value_Has_CorrectValue() + { + HangDumpCommandLineProvider hangDumpCommandLineProvider = GetProvider(); + CommandLineOption option = hangDumpCommandLineProvider.GetCommandLineOptions().First(x => x.Name == HangDumpCommandLineProvider.HangDumpTimeoutOptionName); + + ValidationResult validateOptionsResult = await hangDumpCommandLineProvider.ValidateOptionArgumentsAsync(option, ["32"]).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + public async Task IsInvalid_If_Timeout_Value_Has_IncorrectValue() + { + HangDumpCommandLineProvider hangDumpCommandLineProvider = GetProvider(); + CommandLineOption option = hangDumpCommandLineProvider.GetCommandLineOptions().First(x => x.Name == HangDumpCommandLineProvider.HangDumpTimeoutOptionName); + + ValidationResult validateOptionsResult = await hangDumpCommandLineProvider.ValidateOptionArgumentsAsync(option, ["invalid"]).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(ExtensionResources.HangDumpTimeoutOptionInvalidArgument, validateOptionsResult.ErrorMessage); + } + +#if NETCOREAPP + [Arguments("Triage")] +#endif + [Arguments("Mini")] + [Arguments("Heap")] + [Arguments("Full")] + public async Task IsValid_If_HangDumpType_Has_CorrectValue(string dumpType) + { + HangDumpCommandLineProvider hangDumpCommandLineProvider = GetProvider(); + CommandLineOption option = hangDumpCommandLineProvider.GetCommandLineOptions().First(x => x.Name == HangDumpCommandLineProvider.HangDumpTypeOptionName); + + ValidationResult validateOptionsResult = await hangDumpCommandLineProvider.ValidateOptionArgumentsAsync(option, [dumpType]).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + public async Task IsInvalid_If_HangDumpType_Has_IncorrectValue() + { + HangDumpCommandLineProvider hangDumpCommandLineProvider = GetProvider(); + CommandLineOption option = hangDumpCommandLineProvider.GetCommandLineOptions().First(x => x.Name == HangDumpCommandLineProvider.HangDumpTypeOptionName); + + ValidationResult validateOptionsResult = await hangDumpCommandLineProvider.ValidateOptionArgumentsAsync(option, ["invalid"]).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpTypeOptionInvalidType, "invalid"), validateOptionsResult.ErrorMessage); + } + + [Arguments(HangDumpCommandLineProvider.HangDumpFileNameOptionName)] + [Arguments(HangDumpCommandLineProvider.HangDumpTimeoutOptionName)] + [Arguments(HangDumpCommandLineProvider.HangDumpTypeOptionName)] + public async Task Missing_HangDumpMainOption_ShouldReturn_IsInvalid(string hangDumpArgument) + { + HangDumpCommandLineProvider hangDumpCommandLineProvider = GetProvider(); + var options = new Dictionary + { + { hangDumpArgument, [] }, + }; + + ValidationResult validateOptionsResult = await hangDumpCommandLineProvider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(validateOptionsResult.ErrorMessage, "You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line"); + } + + [Arguments(HangDumpCommandLineProvider.HangDumpFileNameOptionName)] + [Arguments(HangDumpCommandLineProvider.HangDumpTimeoutOptionName)] + [Arguments(HangDumpCommandLineProvider.HangDumpTypeOptionName)] + public async Task If_HangDumpMainOption_IsSpecified_ShouldReturn_IsValid(string hangDumpArgument) + { + HangDumpCommandLineProvider hangDumpCommandLineProvider = GetProvider(); + var options = new Dictionary + { + { hangDumpArgument, [] }, + { HangDumpCommandLineProvider.HangDumpOptionName, [] }, + }; + + ValidationResult validateOptionsResult = await hangDumpCommandLineProvider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestCommandLineOptions.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestCommandLineOptions.cs new file mode 100644 index 0000000000..d999659995 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestCommandLineOptions.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +using Microsoft.Testing.Platform.CommandLine; + +namespace Microsoft.Testing.Extensions.UnitTests.Helpers; + +internal sealed class TestCommandLineOptions : ICommandLineOptions +{ + private readonly Dictionary _options; + + public TestCommandLineOptions(Dictionary options) => _options = options; + + public bool IsOptionSet(string optionName) => _options.ContainsKey(optionName); + + public bool TryGetOptionArgumentList(string optionName, [NotNullWhen(true)] out string[]? arguments) => _options.TryGetValue(optionName, out arguments); +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestExtension.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestExtension.cs new file mode 100644 index 0000000000..85bdd02fc3 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestExtension.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Extensions.UnitTests.Helpers; + +internal sealed class TestExtension : IExtension +{ + public string Uid { get; } = "Uid"; + + public string Version { get; } = "Version"; + + public string DisplayName { get; } = "DisplayName"; + + public string Description { get; } = "Description"; + + public Task IsEnabledAsync() => Task.FromResult(true); +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Microsoft.Testing.Extensions.UnitTests.csproj b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Microsoft.Testing.Extensions.UnitTests.csproj new file mode 100644 index 0000000000..d6dabb68cd --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Microsoft.Testing.Extensions.UnitTests.csproj @@ -0,0 +1,21 @@ + + + + $(MicrosoftTestingTargetFrameworks);net462 + false + + + + + TargetFramework=netstandard2.0 + + + TargetFramework=netstandard2.0 + + + + + + + + diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Program.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Program.cs new file mode 100644 index 0000000000..637390b157 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Program.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions; + +ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); +builder.AddTestFramework(new Microsoft.Testing.Extensions.UnitTests.SourceGeneratedTestNodesBuilder()); +#if ENABLE_CODECOVERAGE +builder.AddCodeCoverageProvider(); +#endif +builder.AddHangDumpProvider(); +builder.AddCrashDumpProvider(ignoreIfNotSupported: true); +builder.AddTrxReportProvider(); +builder.AddAppInsightsTelemetryProvider(); + +// Custom suite tools +CompositeExtensionFactory slowestTestCompositeServiceFactory + = new(_ => new SlowestTestsConsumer()); +builder.TestHost.AddDataConsumer(slowestTestCompositeServiceFactory); +builder.TestHost.AddTestSessionLifetimeHandle(slowestTestCompositeServiceFactory); +ITestApplication app = await builder.BuildAsync(); +return await app.RunAsync(); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Properties/launchSettings.json b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Properties/launchSettings.json new file mode 100644 index 0000000000..ebf3f0f011 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Microsoft.Testing.Extensions.UnitTests": { + "commandName": "Project", + "commandLineArgs": "--treenode-filter /*/*/*/**" + } + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxCompareToolCommandLineTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxCompareToolCommandLineTests.cs new file mode 100644 index 0000000000..ce0d3c10ca --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxCompareToolCommandLineTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.TrxReport.Abstractions; +using Microsoft.Testing.Extensions.UnitTests.Helpers; +using Microsoft.Testing.Platform.Extensions.CommandLine; + +namespace Microsoft.Testing.Extensions.UnitTests; + +[TestGroup] +public class TrxCompareToolCommandLineTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + [Arguments(TrxCompareToolCommandLine.BaselineTrxOptionName)] + [Arguments(TrxCompareToolCommandLine.TrxToCompareOptionName)] + public async Task IsValid_When_Correct_TrxFile_IsProvided_For_Options(string optionName) + { + var provider = new TrxCompareToolCommandLine(new TestExtension()); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == optionName); + string filename = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".trx"); + File.WriteAllText(filename, string.Empty); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [filename]).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + File.Delete(filename); + } + + [Arguments(TrxCompareToolCommandLine.BaselineTrxOptionName, false)] + [Arguments(TrxCompareToolCommandLine.BaselineTrxOptionName, true)] + [Arguments(TrxCompareToolCommandLine.TrxToCompareOptionName, false)] + [Arguments(TrxCompareToolCommandLine.TrxToCompareOptionName, true)] + public async Task IsInvalid_When_Incorrect_TrxFile_IsProvided_For_Options(string optionName, bool isTrxFile) + { + var provider = new TrxCompareToolCommandLine(new TestExtension()); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == optionName); + string filename = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + (isTrxFile ? ".trx" : string.Empty)); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [filename]).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, TestReports.Resources.ExtensionResources.TrxComparerToolOptionExpectsSingleArgument, optionName), validateOptionsResult.ErrorMessage); + } + + public async Task IsValid_If_Both_TrxOptions_Are_Provided() + { + var provider = new TrxCompareToolCommandLine(new TestExtension()); + var options = new Dictionary + { + { TrxCompareToolCommandLine.BaselineTrxOptionName, [] }, + { TrxCompareToolCommandLine.TrxToCompareOptionName, [] }, + }; + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + [Arguments(true, false)] + [Arguments(false, true)] + public async Task IsInvalid_If_Any_TrxOptions_Is_Missing(bool isBaseLineSet, bool isToCompareSet) + { + var provider = new TrxCompareToolCommandLine(new TestExtension()); + var options = new Dictionary(); + if (isBaseLineSet) + { + options.Add(TrxCompareToolCommandLine.BaselineTrxOptionName, []); + } + + if (isToCompareSet) + { + options.Add(TrxCompareToolCommandLine.TrxToCompareOptionName, []); + } + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, TestReports.Resources.ExtensionResources.TrxComparerToolBothFilesMustBeSpecified, TrxCompareToolCommandLine.BaselineTrxOptionName, TrxCompareToolCommandLine.TrxToCompareOptionName), validateOptionsResult.ErrorMessage); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxReportGeneratorCommandLineTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxReportGeneratorCommandLineTests.cs new file mode 100644 index 0000000000..22d7e747f8 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxReportGeneratorCommandLineTests.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.TrxReport.Abstractions; +using Microsoft.Testing.Extensions.UnitTests.Helpers; +using Microsoft.Testing.Platform.CommandLine; + +namespace Microsoft.Testing.Extensions.UnitTests; + +[TestGroup] +public class TrxReportGeneratorCommandLineTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + public async Task IsValid_If_TrxFile_And_Only_TargetFilename_Is_Provided() + { + var provider = new TrxReportGeneratorCommandLine(); + Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == TrxReportGeneratorCommandLine.TrxReportFileNameOptionName); + string filename = Path.GetRandomFileName() + ".trx"; + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [filename]).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + [Arguments(false, false)] + [Arguments(true, true)] + public async Task IsInvalid_If_TrxFile_And_Only_TargetFilename_Are_Not_Provided(bool isTrxFile, bool hasDirectory) + { + var provider = new TrxReportGeneratorCommandLine(); + Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == TrxReportGeneratorCommandLine.TrxReportFileNameOptionName); + + string filename = Path.GetRandomFileName() + (isTrxFile ? ".trx" : string.Empty); + if (hasDirectory) + { + filename = Path.Combine(Path.GetTempPath(), filename); + } + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [filename]).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(isTrxFile ? TestReports.Resources.ExtensionResources.TrxReportFileNameShouldNotContainPath : TestReports.Resources.ExtensionResources.TrxReportFileNameExtensionIsNotTrx, validateOptionsResult.ErrorMessage); + } + + [Arguments(false, false, true)] + [Arguments(true, true, false)] + public async Task IsValid_When_TrxReport_TrxReportFile_Is_Provided_And_DiscoverTests_Not_Provided(bool isFileNameSet, bool isTrxSet, bool isDiscoverTestsSet) + { + var provider = new TrxReportGeneratorCommandLine(); + var options = new Dictionary(); + if (isFileNameSet) + { + options.Add(TrxReportGeneratorCommandLine.TrxReportFileNameOptionName, []); + } + + if (isTrxSet) + { + options.Add(TrxReportGeneratorCommandLine.TrxReportOptionName, []); + } + + if (isDiscoverTestsSet) + { + options.Add(PlatformCommandLineProvider.DiscoverTestsOptionKey, []); + } + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + [Arguments(true, false, false)] + [Arguments(true, true, true)] + public async Task IsInvalid_When_TrxReport_TrxReportFile_Is_Provided_And_DiscoverTests_Provided(bool isFileNameSet, bool isTrxSet, bool isDiscoverTestsSet) + { + var provider = new TrxReportGeneratorCommandLine(); + var options = new Dictionary(); + + if (isFileNameSet) + { + options.Add(TrxReportGeneratorCommandLine.TrxReportFileNameOptionName, []); + } + + if (isTrxSet) + { + options.Add(TrxReportGeneratorCommandLine.TrxReportOptionName, []); + } + + if (isDiscoverTestsSet) + { + options.Add(PlatformCommandLineProvider.DiscoverTestsOptionKey, []); + } + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(isDiscoverTestsSet ? TestReports.Resources.ExtensionResources.TrxReportIsNotValidForDiscovery : TestReports.Resources.ExtensionResources.TrxReportFileNameRequiresTrxReport, validateOptionsResult.ErrorMessage); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs new file mode 100644 index 0000000000..de0012aec3 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs @@ -0,0 +1,443 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Text.RegularExpressions; +using System.Xml.Linq; + +using Microsoft.Testing.Extensions.TrxReport.Abstractions; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Services; +using Microsoft.Testing.Platform.TestHost; + +using Moq; + +using TestNode = Microsoft.Testing.Platform.Extensions.Messages.TestNode; +using TestNodeUid = Microsoft.Testing.Platform.Extensions.Messages.TestNodeUid; + +namespace Microsoft.Testing.Extensions.UnitTests; + +[TestGroup] +public class TrxTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + private readonly Mock _environmentMock = new(); + private readonly Mock _commandLineOptionsMock = new(); + private readonly Mock _configurationMock = new(); + private readonly Mock _clockMock = new(); + private readonly Mock _testFrameworkMock = new(); + private readonly Mock _testApplicationModuleInfoMock = new(); + private readonly Mock _fileSystem = new(); + private readonly Dictionary> _artifactsByTestNode = new(); + private readonly Dictionary> _artifactsByExtension = new(); + + public async Task TrxReportEngine_GenerateReportAsyncWithNullAdapterSupportTrxCapability_TrxDoesNotContainClassName() + { + // Arrange + using MemoryFileStream memoryStream = new(); + PropertyBag propertyBag = new(new PassedTestNodeStateProperty()); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + AssertExpectedTrxFileName(fileName); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Completed"); + string trxContent = xml.ToString(); + Assert.IsFalse(trxContent.Contains(@"className=")); + } + + public async Task TrxReportEngine_GenerateReportAsync_WithArgumentTrxReportFileName_FileIsCorrectlyGenerated() + { + // Arrange + using MemoryFileStream memoryStream = new(); + string[]? argumentTrxReportFileName = ["argumentTrxReportFileName"]; + _ = _commandLineOptionsMock.Setup(_ => _.TryGetOptionArgumentList(TrxReportGeneratorCommandLine.TrxReportFileNameOptionName, out argumentTrxReportFileName)).Returns(true); + PropertyBag propertyBag = new(new PassedTestNodeStateProperty()); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + Assert.IsTrue(fileName.Equals("argumentTrxReportFileName", StringComparison.OrdinalIgnoreCase)); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Completed"); + } + + public async Task TrxReportEngine_GenerateReportAsync_WithInvalidArgumentValueForTrxReportFileName_FileIsGeneratedWithNormalizedName() + { + // Arrange + using MemoryFileStream memoryStream = new(); + string[]? argumentTrxReportFileName = ["NUL"]; + _ = _commandLineOptionsMock.Setup(_ => _.TryGetOptionArgumentList(TrxReportGeneratorCommandLine.TrxReportFileNameOptionName, out argumentTrxReportFileName)).Returns(true); + PropertyBag propertyBag = new(new PassedTestNodeStateProperty()); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + Assert.IsTrue(fileName.Equals("_NUL", StringComparison.OrdinalIgnoreCase)); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Completed"); + } + + public async Task TrxReportEngine_GenerateReportAsync_WithTestHostCrash_ResultSummaryOutcomeIsFailed() + { + // Arrange + using MemoryFileStream memoryStream = new(); + PropertyBag propertyBag = new(new PassedTestNodeStateProperty()); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(isTestHostCrashed: true, keepReportFileStreamOpen: true); + + // Assert + AssertExpectedTrxFileName(fileName); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Failed"); + } + + public async Task TrxReportEngine_GenerateReportAsync_WithTestSkipped_ResultSummaryOutcomeIsCompleted() + { + // Arrange + using MemoryFileStream memoryStream = new(); + PropertyBag propertyBag = new(new SkippedTestNodeStateProperty()); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(0, 0, propertyBag, memoryStream); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + AssertExpectedTrxFileName(fileName); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Completed"); + } + + public async Task TrxReportEngine_GenerateReportAsync_WithTestFailed_WithStandardErrorTrxMessage_TrxContainsStdErr() + { + // Arrange + using MemoryFileStream memoryStream = new(); + PropertyBag propertyBag = new( + new FailedTestNodeStateProperty("test failed"), + new TrxMessagesProperty([new StandardErrorTrxMessage("error message")])); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(0, 1, propertyBag, memoryStream); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + AssertExpectedTrxFileName(fileName); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Failed"); + + XElement? testRun = xml.Root; + Assert.IsNotNull(testRun); + var nodes = testRun.Nodes().ToList(); + + string trxContent = xml.ToString(); + string trxContentsPattern = @" + + + error message + + + "; + Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + } + + public async Task TrxReportEngine_GenerateReportAsync_WithTestFailed_WithoutStandardErrorTrxMessage_TrxContainsStdOut() + { + // Arrange + using MemoryFileStream memoryStream = new(); + PropertyBag propertyBag = new( + new FailedTestNodeStateProperty("test failed"), + new TrxMessagesProperty([new("error message")])); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(0, 1, propertyBag, memoryStream); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + AssertExpectedTrxFileName(fileName); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Failed"); + string trxContent = xml.ToString(); + string trxContentsPattern = @" + + + error message + + + "; + Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + } + + public async Task TrxReportEngine_GenerateReportAsync_WithTestFailed_WithoutStandardErrorTrxMessage_TrxContainsErrorInfo() + { + // Arrange + using MemoryFileStream memoryStream = new(); + PropertyBag propertyBag = new( + new FailedTestNodeStateProperty("test failed"), + new TrxExceptionProperty("trx exception message", "trx stack trace")); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(0, 1, propertyBag, memoryStream); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + AssertExpectedTrxFileName(fileName); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Failed"); + string trxContent = xml.ToString(); + string trxContentsPattern = @" + + + + trx exception message + trx stack trace + + + + "; + Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + } + + public async Task TrxReportEngine_GenerateReportAsync_PassedTestWithTestCategory_TrxContainsTestCategory() + { + // Arrange + using MemoryFileStream memoryStream = new(); + PropertyBag propertyBag = new( + new PassedTestNodeStateProperty(), + new TrxCategoriesProperty(["category1"])); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, propertyBag, memoryStream); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + AssertExpectedTrxFileName(fileName); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Completed"); + string trxContent = xml.ToString(); + string trxContentsPattern = @" + + + + + "; + Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + } + + public async Task TrxReportEngine_GenerateReportAsync_FailedTestWithTestCategory_TrxContainsTestCategory() + { + // Arrange + using MemoryFileStream memoryStream = new(); + PropertyBag propertyBag = new( + new FailedTestNodeStateProperty(), + new TrxCategoriesProperty(["category1"])); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(0, 1, propertyBag, memoryStream); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + AssertExpectedTrxFileName(fileName); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Failed"); + string trxContent = xml.ToString(); + string trxContentsPattern = @" + + + + + "; + Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + } + + public async Task TrxReportEngine_GenerateReportAsync_WithAdapterSupportTrxCapability_TrxContainsClassName() + { + // Arrange + using MemoryFileStream memoryStream = new(); + PropertyBag propertyBag = new( + new PassedTestNodeStateProperty(), + new TrxFullyQualifiedTypeNameProperty("TrxFullyQualifiedTypeName")); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, + propertyBag, memoryStream, true); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + AssertExpectedTrxFileName(fileName); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Completed"); + string trxContent = xml.ToString(); + Assert.That(trxContent.Contains(@"className=""TrxFullyQualifiedTypeName"), trxContent); + } + + public async Task TrxReportEngine_GenerateReportAsync_WithArtifactsByTestNode_TrxContainsResultFile() + { + // Arrange + using MemoryFileStream memoryStream = new(); + _artifactsByTestNode.Add("test()", [new(new SessionUid("1"), new FileInfo("fileName"), "TestMethod", "description")]); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, + new(new PassedTestNodeStateProperty()), memoryStream); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + AssertExpectedTrxFileName(fileName); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Completed"); + string trxContent = xml.ToString(); + string trxContentsPattern = @" + + + + + + "; + Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + } + + public async Task TrxReportEngine_GenerateReportAsync_WithArtifactsByExtension_TrxContainsCollectorDataEntries() + { + // Arrange + using MemoryFileStream memoryStream = new(); + _artifactsByExtension.Add( + new ToolTrxCompareFactory(), + [new(new SessionUid("1"), new FileInfo("fileName"), "TestMethod", "description")]); + TrxReportEngine trxReportEngine = GenerateTrxReportEngine(1, 0, + new(new PassedTestNodeStateProperty()), memoryStream); + + // Act + string fileName = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + AssertExpectedTrxFileName(fileName); + XDocument xml = GetTrxContent(memoryStream); + AssertTrxOutcome(xml, "Completed"); + string trxContent = xml.ToString(); + string trxContentsPattern = @" + + + + + + + + + + "; + Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + } + + public async Task TrxReportEngine_GenerateReportAsync_FileAlreadyExists_WillRetry() + { + // Arrange + int retryCount = 0; + _ = _fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.CreateNew)) + .Returns(() => + { + if (retryCount > 3) + { + return new MemoryFileStream(); + } + + retryCount++; + throw new IOException("The process cannot access the file because it is being used by another process."); + }); + + _ = _configurationMock.SetupGet(_ => _[It.IsAny()]).Returns(string.Empty); + _ = _environmentMock.SetupGet(_ => _.MachineName).Returns("MachineName"); + _ = _testApplicationModuleInfoMock.Setup(_ => _.GetCurrentTestApplicationFullPath()).Returns("TestAppPath"); + TrxReportEngine trxReportEngine = new(_fileSystem.Object, _testApplicationModuleInfoMock.Object, _environmentMock.Object, _commandLineOptionsMock.Object, + _configurationMock.Object, _clockMock.Object, [], 0, 0, + _artifactsByExtension, _artifactsByTestNode, true, _testFrameworkMock.Object, DateTime.UtcNow, CancellationToken.None, + isCopyingFileAllowed: false); + + // Act + _ = await trxReportEngine.GenerateReportAsync(keepReportFileStreamOpen: true); + + // Assert + Assert.AreEqual(4, retryCount); + } + + private static XDocument GetTrxContent(MemoryFileStream memoryStream) + { + Assert.IsNotNull(memoryStream); + _ = memoryStream.Stream.Seek(0, SeekOrigin.Begin); + return XDocument.Load(memoryStream.Stream); + } + + private static void AssertTrxOutcome(XDocument xml, string expectedOutcome) + { + Assert.IsNotNull(xml); + XElement? testRun = xml.Root; + Assert.IsNotNull(testRun); + var resultSummary = (XElement?)testRun.LastNode; + Assert.IsNotNull(resultSummary); + XAttribute? outcome = resultSummary.FirstAttribute; + Assert.IsNotNull(outcome); + Assert.IsNotNull(outcome.Value); + Assert.IsTrue(outcome.Value.Equals(expectedOutcome, StringComparison.Ordinal)); + } + + private static void AssertExpectedTrxFileName(string fileName) + => Assert.IsTrue(fileName.Equals("_MachineName_0001-01-01_00_00_00.000.trx", StringComparison.Ordinal)); + + private TrxReportEngine GenerateTrxReportEngine(int passedTestsCount, int failedTestsCount, PropertyBag propertyBag, MemoryFileStream memoryStream, + bool? adapterSupportTrxCapability = null) + { + var testNode = new TestNodeUpdateMessage( + new SessionUid("1"), + new TestNode { Uid = new TestNodeUid("test()"), DisplayName = "TestMethod", Properties = propertyBag }); + + TestNodeUpdateMessage[] testNodeUpdatedMessages = [testNode]; + + DateTime testStartTime = DateTime.Now; + CancellationToken cancellationToken = CancellationToken.None; + + _ = _fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(true); + _ = _fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.CreateNew)) + .Returns(memoryStream); + + _ = _configurationMock.SetupGet(_ => _[It.IsAny()]).Returns(string.Empty); + _ = _environmentMock.SetupGet(_ => _.MachineName).Returns("MachineName"); + _ = _testApplicationModuleInfoMock.Setup(_ => _.GetCurrentTestApplicationFullPath()).Returns("TestAppPath"); + + return new TrxReportEngine(_fileSystem.Object, _testApplicationModuleInfoMock.Object, _environmentMock.Object, _commandLineOptionsMock.Object, + _configurationMock.Object, _clockMock.Object, testNodeUpdatedMessages, failedTestsCount, passedTestsCount, + _artifactsByExtension, _artifactsByTestNode, adapterSupportTrxCapability, _testFrameworkMock.Object, testStartTime, cancellationToken, + isCopyingFileAllowed: false); + } + + private sealed class MemoryFileStream : IFileStream + { + public MemoryFileStream() + { + Stream = new MemoryStream(); + } + + public MemoryStream Stream { get; } + + Stream IFileStream.Stream => Stream; + + string IFileStream.Name => string.Empty; + + void IDisposable.Dispose() + => Stream.Dispose(); + +#if NETCOREAPP + ValueTask IAsyncDisposable.DisposeAsync() + => Stream.DisposeAsync(); +#endif + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/RunSettingsCommandLineOptionsProviderTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/RunSettingsCommandLineOptionsProviderTests.cs new file mode 100644 index 0000000000..03f91ef640 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/RunSettingsCommandLineOptionsProviderTests.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; +using Microsoft.Testing.Extensions.VSTestBridge.Resources; +using Microsoft.Testing.Extensions.VSTestBridge.UnitTests.Helpers; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Helpers; + +using Moq; + +namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.CommandLine; + +[TestGroup] +public sealed class RunSettingsCommandLineOptionsProviderTests(ITestExecutionContext testExecutionContext) + : TestBase(testExecutionContext) +{ + public async Task RunSettingsOption_WhenFileDoesNotExist_IsNotValid() + { + // Arrange + const string filePath = "file"; + var fileSystem = new Mock(MockBehavior.Strict); + fileSystem.Setup(fs => fs.Exists(It.IsAny())).Returns(false); + + var provider = new RunSettingsCommandLineOptionsProvider(new TestExtension(), fileSystem.Object); + CommandLineOption option = provider.GetCommandLineOptions().Single(); + + // Act + ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, [filePath]); + + // Assert + Assert.IsFalse(result.IsValid); + Assert.AreEqual(string.Format(CultureInfo.CurrentCulture, ExtensionResources.RunsettingsFileDoesNotExist, filePath), result.ErrorMessage); + } + + public async Task RunSettingsOption_WhenFileCannotBeOpen_IsNotValid() + { + // Arrange + const string filePath = "file"; + var fileSystem = new Mock(MockBehavior.Strict); + fileSystem.Setup(fs => fs.Exists(filePath)).Returns(true); + fileSystem.Setup(fs => fs.NewFileStream(filePath, FileMode.Open, FileAccess.Read)).Throws(new IOException()); + + var provider = new RunSettingsCommandLineOptionsProvider(new TestExtension(), fileSystem.Object); + CommandLineOption option = provider.GetCommandLineOptions().Single(); + + // Act + ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, [filePath]); + + // Assert + Assert.IsFalse(result.IsValid); + Assert.AreEqual(string.Format(CultureInfo.CurrentCulture, ExtensionResources.RunsettingsFileCannotBeRead, filePath), result.ErrorMessage); + } + + public async Task RunSettingsOption_WhenFileExistsAndCanBeOpen_IsValid() + { + // Arrange + const string filePath = "file"; + var fileSystem = new Mock(MockBehavior.Strict); + fileSystem.Setup(fs => fs.Exists(filePath)).Returns(true); + fileSystem.Setup(fs => fs.NewFileStream(filePath, FileMode.Open, FileAccess.Read)).Returns(new Mock().Object); + + var provider = new RunSettingsCommandLineOptionsProvider(new TestExtension(), fileSystem.Object); + CommandLineOption option = provider.GetCommandLineOptions().Single(); + + // Act + ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, [filePath]); + + // Assert + Assert.IsTrue(result.IsValid); + Assert.IsNull(result.ErrorMessage); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/TestRunParameterCommandLineOptionsProviderTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/TestRunParameterCommandLineOptionsProviderTests.cs new file mode 100644 index 0000000000..7677aef16d --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/TestRunParameterCommandLineOptionsProviderTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; +using Microsoft.Testing.Extensions.VSTestBridge.Resources; +using Microsoft.Testing.Extensions.VSTestBridge.UnitTests.Helpers; +using Microsoft.Testing.Platform.Extensions.CommandLine; + +namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.CommandLine; + +[TestGroup] +public sealed class TestRunParameterCommandLineOptionsProviderTests(ITestExecutionContext testExecutionContext) + : TestBase(testExecutionContext) +{ + public async Task TestRunParameterOption_WhenArgumentDoesNotContainEqual_IsNotValid() + { + // Arrange + var provider = new TestRunParametersCommandLineOptionsProvider(new TestExtension()); + CommandLineOption option = provider.GetCommandLineOptions().Single(); + + // Act + ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, ["something"]); + + // Assert + Assert.IsFalse(result.IsValid); + Assert.AreEqual(string.Format(CultureInfo.CurrentCulture, ExtensionResources.TestRunParameterOptionArgumentIsNotParameter, "something"), result.ErrorMessage); + } + + public async Task TestRunParameterOption_WhenArgumentContainsEqual_IsValid() + { + // Arrange + var provider = new TestRunParametersCommandLineOptionsProvider(new TestExtension()); + CommandLineOption option = provider.GetCommandLineOptions().Single(); + + // Act + ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, ["a=b"]); + + // Assert + Assert.IsTrue(result.IsValid); + Assert.IsNull(result.ErrorMessage); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Helpers/TestExtension.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Helpers/TestExtension.cs new file mode 100644 index 0000000000..fd087790e7 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Helpers/TestExtension.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.Helpers; + +internal sealed class TestExtension : IExtension +{ + public string Uid { get; } = "Uid"; + + public string Version { get; } = "Version"; + + public string DisplayName { get; } = "DisplayName"; + + public string Description { get; } = "Description"; + + public Task IsEnabledAsync() => Task.FromResult(true); +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.csproj b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.csproj new file mode 100644 index 0000000000..a829f24bbf --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.csproj @@ -0,0 +1,16 @@ + + + + $(TargetFrameworks);net462 + false + + + + + + + + + + + diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.launcher.config.json b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.launcher.config.json new file mode 100644 index 0000000000..066f77ebe3 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.launcher.config.json @@ -0,0 +1,3 @@ +{ + "program": "Microsoft.Testing.Extensions.VSTestBridge.UnitTests.exe" +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.testingplatformconfig.json b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.testingplatformconfig.json new file mode 100644 index 0000000000..4c41cda3a3 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.testingplatformconfig.json @@ -0,0 +1,8 @@ +{ + "testingplatform": { + "telemetry": { + "isDevelopmentRepository": true + }, + "exitProcessOnUnhandledException": true + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.cs new file mode 100644 index 0000000000..0aae12cd59 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.cs @@ -0,0 +1,278 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.TrxReport.Abstractions; +using Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.TestHost; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; + +namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.ObjectModel; + +[TestGroup] +public sealed class ObjectModelConvertersTests : TestBase +{ + private static readonly ClientInfo TestClient = new("UnitTest", string.Empty); + private static readonly ClientInfo VSTestClient = new(WellKnownClients.VisualStudio, string.Empty); + + public ObjectModelConvertersTests(ITestExecutionContext testExecutionContext) + : base(testExecutionContext) + { + } + + public void ToTestNode_WhenTestCaseHasTestNodeUidProperty_TestNodeUidUsesIt() + { + TestCase testCase = new("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs"); + testCase.SetPropertyValue(ObjectModelConverters.TestNodeUidProperty, "SomeUid"); + var testNode = testCase.ToTestNode(false, TestClient); + + Assert.AreEqual("SomeUid", testNode.Uid.Value); + } + + public void ToTestNode_WhenTestCaseHasNoTestNodeUidProperty_TestNodeUidUsesFqn() + { + TestCase testCase = new("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs"); + var testNode = testCase.ToTestNode(false, TestClient); + + Assert.AreEqual("SomeFqn", testNode.Uid.Value); + } + + public void ToTestNode_WhenTestCaseHasDisplayName_TestNodeDisplayNameUsesIt() + { + TestCase testCase = new("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs") + { + DisplayName = "MyDisplayName", + }; + var testNode = testCase.ToTestNode(false, TestClient); + + Assert.AreEqual("MyDisplayName", testNode.DisplayName); + } + + public void ToTestNode_WhenTestCaseHasNoDisplayName_TestNodeDisplayNameUsesIt() + { + TestCase testCase = new("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs"); + var testNode = testCase.ToTestNode(false, TestClient); + + Assert.AreEqual("SomeFqn", testNode.DisplayName); + } + + public void ToTestNode_WhenTestResultHasCodeFilePath_SetsTestFileLocationProperty() + { + TestResult testResult = new(new("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs") + { + CodeFilePath = "FilePath", + }); + var testNode = testResult.ToTestNode(false, TestClient); + Assert.AreEqual("FilePath", testNode.Properties.Single().FilePath); + } + + public void ToTestNode_WhenTestResultHasTestNodeUidProperty_TestNodeUidUsesIt() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")); + testResult.TestCase.SetPropertyValue(ObjectModelConverters.TestNodeUidProperty, "SomeUid"); + var testNode = testResult.ToTestNode(false, TestClient); + + Assert.AreEqual("SomeUid", testNode.Uid.Value); + } + + public void ToTestNode_WhenTestResultOutcomeIsFailed_TestNodePropertiesContainFailedTestNodeStateProperty() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) + { + Outcome = TestOutcome.Failed, + ErrorMessage = "SomeErrorMessage", + ErrorStackTrace = "SomeStackTrace", + }; + var testNode = testResult.ToTestNode(false, TestClient); + + FailedTestNodeStateProperty[] failedTestNodeStateProperties = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(failedTestNodeStateProperties.Length == 1); + Assert.IsTrue(failedTestNodeStateProperties[0].Exception is VSTestException); + Assert.AreEqual(testResult.ErrorStackTrace, failedTestNodeStateProperties[0].Exception!.StackTrace); + Assert.AreEqual(testResult.ErrorMessage, failedTestNodeStateProperties[0].Exception!.Message); + } + + public void ToTestNode_WhenTestResultHasMSTestDiscovererTestCategoryTestProperty_TestNodePropertiesContainTheCategoryInTraits() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")); + var testCategoryProperty = TestProperty.Register("MSTestDiscoverer.TestCategory", "Label", typeof(string[]), TestPropertyAttributes.None, typeof(TestCase)); + testResult.SetPropertyValue(testCategoryProperty, ["category1"]); + + var testNode = testResult.ToTestNode(false, VSTestClient); + + SerializableNamedKeyValuePairsStringProperty[] errorTestNodeStateProperties = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(errorTestNodeStateProperties.Length == 1); + Assert.IsTrue(errorTestNodeStateProperties[0].Name == "traits"); + Assert.IsTrue(errorTestNodeStateProperties[0].Pairs.Length == 1); + Assert.IsTrue(errorTestNodeStateProperties[0].Pairs[0].Key == "category1"); + } + + public void ToTestNode_WhenTestResultHasMSTestDiscovererTestCategoryTestPropertyWithTrxEnabled_TestNodePropertiesContainTrxCategoriesProperty() + { + TestResult testResult = new(new TestCase("assembly.class.SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")); + var testCategoryProperty = TestProperty.Register("MSTestDiscoverer.TestCategory", "Label", typeof(string[]), TestPropertyAttributes.None, typeof(TestCase)); + testResult.SetPropertyValue(testCategoryProperty, ["category1"]); + + var testNode = testResult.ToTestNode(true, VSTestClient); + + TrxCategoriesProperty[] trxCategoriesProperty = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(trxCategoriesProperty.Length == 1); + Assert.IsTrue(trxCategoriesProperty[0].Categories.Length == 1); + Assert.AreEqual(trxCategoriesProperty[0].Categories[0], "category1"); + } + + public void ToTestNode_WhenTestResultHasTestCaseHierarchyTestProperty_TestNodePropertiesContainItInSerializableNamedArrayStringProperty() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")); + var testCaseHierarchy = TestProperty.Register("TestCase.Hierarchy", "Label", typeof(string[]), TestPropertyAttributes.None, typeof(TestCase)); + testResult.SetPropertyValue(testCaseHierarchy, ["assembly", "class", "category", "test"]); + + var testNode = testResult.ToTestNode(false, VSTestClient); + + SerializableNamedArrayStringProperty[] trxCategoriesProperty = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(trxCategoriesProperty.Length == 1); + Assert.AreEqual(trxCategoriesProperty[0].Values[0], "assembly"); + Assert.AreEqual(trxCategoriesProperty[0].Values[1], "class"); + Assert.AreEqual(trxCategoriesProperty[0].Values[2], "category"); + Assert.AreEqual(trxCategoriesProperty[0].Values[3], "test"); + } + + public void ToTestNode_WhenTestResultHasOriginalExecutorUriProperty_TestNodePropertiesContainItInSerializableKeyValuePairStringProperty() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")); + var originalExecutorUriProperty = TestProperty.Register( + VSTestTestNodeProperties.OriginalExecutorUriPropertyName, VSTestTestNodeProperties.OriginalExecutorUriPropertyName, typeof(Uri), typeof(TestCase)); + + testResult.SetPropertyValue(originalExecutorUriProperty, new Uri("https://vs.com/")); + + var testNode = testResult.ToTestNode(false, VSTestClient); + + SerializableKeyValuePairStringProperty[] serializableKeyValuePairStringProperty = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(serializableKeyValuePairStringProperty.Length == 3); + Assert.AreEqual(serializableKeyValuePairStringProperty[0].Key, VSTestTestNodeProperties.OriginalExecutorUriPropertyName); + Assert.AreEqual(serializableKeyValuePairStringProperty[0].Value, "https://vs.com/"); + } + + public void ToTestNode_WhenTestResultHasFullyQualifiedTypeAndTrxEnabled_TestNodeHasFullyQualifiedTypeName() + { + TestResult testResult = new(new TestCase("assembly.class.test", new("executor://uri", UriKind.Absolute), "source.cs")); + + var testNode = testResult.ToTestNode(true, TestClient); + + Assert.AreEqual(testNode.Properties.OfType()?.Length, 1); + Assert.AreEqual("assembly.class", testNode.Properties.Single().FullyQualifiedTypeName); + } + + public void ToTestNode_WhenTestResultHasNoFullyQualifiedTypeAndTrxEnabled_Throws() + { + TestResult testResult = new(new TestCase("test", new("executor://uri", UriKind.Absolute), "source.cs")); + + string errorMessage = Assert.Throws(() => testResult.ToTestNode(true, TestClient)).Message; + + Assert.IsTrue(errorMessage.Contains("Unable to parse fully qualified type name from test case: ")); + } + + public void ToTestNode_FromTestResult_TestNodePropertiesContainCorrectTimingProperty() + { + var startTime = new DateTime(1996, 8, 22, 20, 30, 5); + var endTime = new DateTime(1996, 8, 22, 20, 31, 5); + var duration = new TimeSpan(0, 1, 0); + + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) + { + StartTime = startTime, + EndTime = endTime, + Duration = duration, + }; + var testNode = testResult.ToTestNode(false, TestClient); + var testResultTimingProperty = new TimingProperty(new(startTime, endTime, duration), []); + + Assert.AreEqual(testNode.Properties.OfType()[0], testResultTimingProperty); + } + + public void ToTestNode_WhenTestResultOutcomeIsNotFoundWithoutSetErrorMessage_TestNodePropertiesContainErrorTestNodeStatePropertyWithDefaultErrorMessage() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) + { + Outcome = TestOutcome.NotFound, + ErrorStackTrace = "SomeStackTrace", + }; + var testNode = testResult.ToTestNode(false, TestClient); + + ErrorTestNodeStateProperty[] errorTestNodeStateProperties = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(errorTestNodeStateProperties.Length == 1); + Assert.IsTrue(errorTestNodeStateProperties[0].Exception is VSTestException); + Assert.AreEqual(testResult.ErrorStackTrace, errorTestNodeStateProperties[0].Exception!.StackTrace); + Assert.IsTrue(errorTestNodeStateProperties[0].Exception!.Message.Contains("Not found")); + } + + public void ToTestNode_WhenTestResultOutcomeIsSkipped_TestNodePropertiesContainSkippedTestNodeStateProperty() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) + { + Outcome = TestOutcome.Skipped, + }; + var testNode = testResult.ToTestNode(false, TestClient); + + SkippedTestNodeStateProperty[] skipTestNodeStateProperties = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(skipTestNodeStateProperties.Length == 1); + } + + public void ToTestNode_WhenTestResultOutcomeIsNone_TestNodePropertiesContainSkippedTestNodeStateProperty() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) + { + Outcome = TestOutcome.None, + }; + var testNode = testResult.ToTestNode(false, TestClient); + + SkippedTestNodeStateProperty[] skipTestNodeStateProperties = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(skipTestNodeStateProperties.Length == 1); + } + + public void ToTestNode_WhenTestResultOutcomeIsPassed_TestNodePropertiesContainPassedTestNodeStateProperty() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) + { + Outcome = TestOutcome.Passed, + }; + var testNode = testResult.ToTestNode(false, TestClient); + + PassedTestNodeStateProperty[] passedTestNodeStateProperties = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(passedTestNodeStateProperties.Length == 1); + } + + public void ToTestNode_WhenTestResultHasUidAndDisplayNameWithWellKnownClient_TestNodePropertiesContainSerializableKeyValuePairStringPropertyTwice() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) + { + DisplayName = "TestName", + }; + + var testNode = testResult.ToTestNode(false, VSTestClient); + + SerializableKeyValuePairStringProperty[] errorTestNodeStateProperties = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(errorTestNodeStateProperties.Length == 2, "Expected 2 SerializableKeyValuePairStringProperty"); + Assert.IsTrue(errorTestNodeStateProperties[0].Key == "vstest.TestCase.Id"); + Assert.IsTrue(errorTestNodeStateProperties[1].Key == "vstest.TestCase.FullyQualifiedName"); + Assert.IsTrue(errorTestNodeStateProperties[1].Value == "SomeFqn"); + } + + public void ToTestNode_WhenTestResultHasTraits_TestNodePropertiesContainIt() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) + { + DisplayName = "TestName", + Traits = { new Trait("key", "value") }, + }; + + var testNode = testResult.ToTestNode(false, VSTestClient); + + SerializableNamedKeyValuePairsStringProperty[] errorTestNodeStateProperties = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(errorTestNodeStateProperties.Length == 1); + Assert.IsTrue(errorTestNodeStateProperties[0].Name == "traits"); + Assert.IsTrue(errorTestNodeStateProperties[0].Pairs.Length == 1); + Assert.IsTrue(errorTestNodeStateProperties[0].Pairs[0].Key == "key"); + Assert.IsTrue(errorTestNodeStateProperties[0].Pairs[0].Value == "value"); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunContextAdapterTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunContextAdapterTests.cs new file mode 100644 index 0000000000..543e1012bb --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunContextAdapterTests.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + +using Moq; + +namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.ObjectModel; + +[TestGroup] +public class RunContextAdapterTests : TestBase +{ + private readonly Mock _commandLineOptions = new(); + private readonly Mock _runSettings = new(); + + public RunContextAdapterTests(ITestExecutionContext testExecutionContext) + : base(testExecutionContext) + { + } + + public void TestRunDirectory_IsNotNull_If_ResultsDirectory_Is_Provided() + { + string runSettings = +$""" + + + /PlatformResultDirectoryFromFile + + +"""; + + _runSettings.Setup(x => x.SettingsXml).Returns(runSettings); + RunContextAdapter runContextAdapter = new(_commandLineOptions.Object, _runSettings.Object); + Assert.AreEqual("/PlatformResultDirectoryFromFile", runContextAdapter.TestRunDirectory); + Assert.AreEqual(runSettings, runContextAdapter.RunSettings!.SettingsXml); + Assert.IsNotNull(runContextAdapter.RunSettings); + } + + public void TestRunDirectory_IsNull_If_ResultsDirectory_IsNot_Provided() + { + string runSettings = +$""" + + + + +"""; + + _runSettings.Setup(x => x.SettingsXml).Returns(runSettings); + RunContextAdapter runContextAdapter = new(_commandLineOptions.Object, _runSettings.Object); + Assert.IsNull(runContextAdapter.TestRunDirectory); + Assert.IsNotNull(runContextAdapter.RunSettings); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunSettingsPatcherTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunSettingsPatcherTests.cs new file mode 100644 index 0000000000..8d82114de1 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunSettingsPatcherTests.cs @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Xml.Linq; +using System.Xml.XPath; + +using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; +using Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; +using Microsoft.Testing.Platform.TestHost; + +using Moq; + +namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.ObjectModel; + +[TestGroup] +public class RunSettingsPatcherTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + private readonly Mock _configuration = new(); + private readonly Mock _commandLineOptions = new(); + + public void Patch_WhenNoRunSettingsProvided_CreateRunSettingsWithResultsDirectoryElement() + { + _configuration.Setup(x => x[PlatformConfigurationConstants.PlatformResultDirectory]).Returns("/PlatformResultDirectory"); + XDocument runSettingsDocument = RunSettingsPatcher.Patch(null, _configuration.Object, + new ClientInfo(string.Empty, string.Empty), _commandLineOptions.Object); + Assert.AreEqual( + "/PlatformResultDirectory", + runSettingsDocument.XPathSelectElement("RunSettings/RunConfiguration/ResultsDirectory")!.Value); + } + + public void Patch_WithRunSettingsProvidedButMissingResultsDirectory_AddsElement() + { + string runSettings = """ + + + true + + + """; + + _configuration.Setup(x => x[PlatformConfigurationConstants.PlatformResultDirectory]).Returns("/PlatformResultDirectory"); + + XDocument runSettingsDocument = RunSettingsPatcher.Patch(runSettings, _configuration.Object, new ClientInfo(string.Empty, string.Empty), _commandLineOptions.Object); + Assert.AreEqual( + "/PlatformResultDirectory", + runSettingsDocument.XPathSelectElement("RunSettings/RunConfiguration/ResultsDirectory")!.Value); + Assert.IsTrue(bool.Parse(runSettingsDocument.XPathSelectElement("RunSettings/RunConfiguration/Canary")!.Value)); + } + + public void Patch_WithRunSettingsContainingResultsDirectory_EntryIsNotOverridden() + { + string runSettings = +$""" + + + true + /PlatformResultDirectoryFromFile + + +"""; + + _configuration.Setup(x => x[PlatformConfigurationConstants.PlatformResultDirectory]).Returns("/PlatformResultDirectory"); + XDocument runSettingsDocument = RunSettingsPatcher.Patch(runSettings, _configuration.Object, new ClientInfo(string.Empty, string.Empty), _commandLineOptions.Object); + Assert.AreEqual( + "/PlatformResultDirectoryFromFile", + runSettingsDocument.XPathSelectElement("RunSettings/RunConfiguration/ResultsDirectory")!.Value); + Assert.IsTrue(bool.Parse(runSettingsDocument.XPathSelectElement("RunSettings/RunConfiguration/Canary")!.Value)); + } + + public void Patch_WhenRunSettingsExists_MergesParameters() + { + string runSettings = """ + + + + + + + """ + ; + + string[]? arguments; + _commandLineOptions.Setup(x => x.TryGetOptionArgumentList(TestRunParametersCommandLineOptionsProvider.TestRunParameterOptionName, out arguments)) + .Returns((string optionName, out string[]? arguments) => + { + arguments = ["key2=updated-value", "key3=value3"]; + return true; + }); + + _configuration.Setup(x => x[PlatformConfigurationConstants.PlatformResultDirectory]).Returns("/PlatformResultDirectory"); + XDocument runSettingsDocument = RunSettingsPatcher.Patch(runSettings, _configuration.Object, new ClientInfo(string.Empty, string.Empty), + _commandLineOptions.Object); + + XElement[] testRunParameters = runSettingsDocument.XPathSelectElements("RunSettings/TestRunParameters/Parameter").ToArray(); + Assert.AreEqual(testRunParameters[0].Attribute("name")!.Value, "key1"); + Assert.AreEqual(testRunParameters[0].Attribute("value")!.Value, "value1"); + Assert.AreEqual(testRunParameters[1].Attribute("name")!.Value, "key2"); + Assert.AreEqual(testRunParameters[1].Attribute("value")!.Value, "updated-value"); + Assert.AreEqual(testRunParameters[2].Attribute("name")!.Value, "key3"); + Assert.AreEqual(testRunParameters[2].Attribute("value")!.Value, "value3"); + } + + public void Patch_WhenRunSettingsDoesNotExist_AddParameters() + { + string[]? arguments; + _commandLineOptions.Setup(x => x.TryGetOptionArgumentList(TestRunParametersCommandLineOptionsProvider.TestRunParameterOptionName, out arguments)) + .Returns((string optionName, out string[]? arguments) => + { + arguments = ["key1=value1", "key2=value2"]; + return true; + }); + + _configuration.Setup(x => x[PlatformConfigurationConstants.PlatformResultDirectory]).Returns("/PlatformResultDirectory"); + XDocument runSettingsDocument = RunSettingsPatcher.Patch(null, _configuration.Object, new ClientInfo(string.Empty, string.Empty), + _commandLineOptions.Object); + + XElement[] testRunParameters = runSettingsDocument.XPathSelectElements("RunSettings/TestRunParameters/Parameter").ToArray(); + Assert.AreEqual(testRunParameters[0].Attribute("name")!.Value, "key1"); + Assert.AreEqual(testRunParameters[0].Attribute("value")!.Value, "value1"); + Assert.AreEqual(testRunParameters[1].Attribute("name")!.Value, "key2"); + Assert.AreEqual(testRunParameters[1].Attribute("value")!.Value, "value2"); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Program.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Program.cs new file mode 100644 index 0000000000..0b997618f7 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Program.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions; +using Microsoft.Testing.Extensions.VSTestBridge.UnitTests; + +ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); +builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); + +#if NETCOREAPP +Console.WriteLine("Dynamic code supported: " + System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported); +#endif + +#if !NATIVE_AOT +#if ENABLE_CODECOVERAGE +builder.AddCodeCoverageProvider(); +#endif +builder.AddAppInsightsTelemetryProvider(); +builder.AddHangDumpProvider(); +Console.WriteLine("NATIVE_AOT disabled"); +#else +Console.WriteLine("NATIVE_AOT enabled"); +#endif + +builder.AddCrashDumpProvider(ignoreIfNotSupported: true); +builder.AddTrxReportProvider(); + +// Custom suite tools +CompositeExtensionFactory slowestTestCompositeServiceFactory + = new(_ => new SlowestTestsConsumer()); +builder.TestHost.AddDataConsumer(slowestTestCompositeServiceFactory); +builder.TestHost.AddTestSessionLifetimeHandle(slowestTestCompositeServiceFactory); +using ITestApplication app = await builder.BuildAsync(); +return await app.RunAsync(); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Properties/launchSettings.json b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Properties/launchSettings.json new file mode 100644 index 0000000000..ea079cb247 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "Microsoft.Testing.Extensions.VSTestBridge.UnitTests": { + "commandName": "Project", + "commandLineArgs": "--treenode-filter /*/*/*/**", + "environmentVariables": { + } + } + } +} diff --git a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/MSBuildTests.cs b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/MSBuildTests.cs new file mode 100644 index 0000000000..a05b0bb210 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/MSBuildTests.cs @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections; + +using Microsoft.Build.Framework; +#if NET8_0_OR_GREATER +using Moq; +#endif + +namespace Microsoft.Testing.Platform.MSBuild.UnitTests; + +[TestGroup] +public class MSBuildTests : TestBase +{ +#if NET8_0_OR_GREATER + private readonly Mock _buildEngine; + private readonly List _errors; +#endif + + public MSBuildTests(ITestExecutionContext testExecutionContext) + : base(testExecutionContext) + { +#if NET8_0_OR_GREATER + _buildEngine = new Mock(); + _errors = new List(); + _buildEngine.Setup(x => x.LogErrorEvent(It.IsAny())).Callback(e => _errors.Add(e)); +#endif + } + + public void Verify_Correct_Registration_Order_For_WellKnown_Extensions() + { +#if !NET8_0_OR_GREATER + // On netfx, net6.0, and net7.0 this is failing with: + // Could not load file or assembly 'Microsoft.Build.Framework, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified. + // This is because the NuGet Package is "compatible" with netstandard2.0, so it can be installed everywhere, but it restores dlls only into specific (new) versions of .NET Framework and .NET. + return; +#else + InMemoryFileSystem inMemoryFileSystem = new(); + TestingPlatformEntryPointTask testingPlatformEntryPoint = new(inMemoryFileSystem) + { + BuildEngine = _buildEngine.Object, + TestingPlatformEntryPointSourcePath = new CustomTaskItem("obj/entryPointFile"), + Language = new CustomTaskItem("C#"), + TestingPlatformBuilderHooks = new List() + { + new CustomTaskItem("95914C54-6C6E-4AF7-9327-4905E1CE9DB7") + .Add("DisplayName", "DisplayName") + .Add("TypeFullName", "TypeFullName"), + + // Microsoft.Testing.Extensions.TrxReport + new CustomTaskItem("2006B3F7-93D2-4D9C-9C69-F41A1F21C9C7") + .Add("DisplayName", "DisplayName") + .Add("TypeFullName", "Microsoft.Testing.Extensions.TrxReport"), + + new CustomTaskItem("95914C54-6C6E-4AF7-9327-4905E1CE9DB9") + .Add("DisplayName", "DisplayName") + .Add("TypeFullName", "TypeFullName"), + }.ToArray(), + }; + + testingPlatformEntryPoint.Execute(); + + string expectedSourceOrder = """ +//------------------------------------------------------------------------------ +// +// This code was generated by Microsoft.Testing.Platform.MSBuild +// +//------------------------------------------------------------------------------ + +[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] +internal sealed class TestingPlatformEntryPoint +{ + public static async global::System.Threading.Tasks.Task Main(string[] args) + { + global::Microsoft.Testing.Platform.Builder.ITestApplicationBuilder builder = await global::Microsoft.Testing.Platform.Builder.TestApplication.CreateBuilderAsync(args); + TypeFullName.AddExtensions(builder, args); + TypeFullName.AddExtensions(builder, args); + Microsoft.Testing.Extensions.TrxReport.AddExtensions(builder, args); + using (global::Microsoft.Testing.Platform.Builder.ITestApplication app = await builder.BuildAsync()) + { + return await app.RunAsync(); + } + } +} +"""; + + Assert.AreEqual(expectedSourceOrder, inMemoryFileSystem.Files["obj/entryPointFile"]); +#endif + } + + private sealed class InMemoryFileSystem : IFileSystem + { + public Dictionary Files { get; } = new(); + + public void CopyFile(string source, string destination) => throw new NotImplementedException(); + + public void CreateDirectory(string directory) => throw new NotImplementedException(); + + public Stream CreateNew(string path) => throw new NotImplementedException(); + + public bool Exist(string path) => Files.ContainsKey(path); + + public void WriteAllText(string path, string? contents) => Files.Add(path, contents); + } + + private sealed class CustomTaskItem : ITaskItem + { + private readonly Dictionary _keyValuePairs = new(); + + public CustomTaskItem(string itemSpec) + { + ItemSpec = itemSpec; + } + + public CustomTaskItem Add(string key, string value) + { + _keyValuePairs[key] = value; + return this; + } + + public string ItemSpec { get; set; } + + public ICollection MetadataNames => throw new NotImplementedException(); + + public int MetadataCount => _keyValuePairs.Count; + + public IDictionary CloneCustomMetadata() => throw new NotImplementedException(); + + public void CopyMetadataTo(ITaskItem destinationItem) => throw new NotImplementedException(); + + public string? GetMetadata(string metadataName) => _keyValuePairs.TryGetValue(metadataName, out string? value) ? value : null; + + public void RemoveMetadata(string metadataName) => throw new NotImplementedException(); + + public void SetMetadata(string metadataName, string metadataValue) => throw new NotImplementedException(); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests.csproj b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests.csproj new file mode 100644 index 0000000000..70d8498495 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests.csproj @@ -0,0 +1,18 @@ + + + + $(MicrosoftTestingTargetFrameworks);net462 + false + + + + + + + + + + + + + diff --git a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Program.cs b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Program.cs new file mode 100644 index 0000000000..45d399bd11 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Program.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions; + +// DebuggerUtility.AttachVSToCurrentProcess(); +ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); +builder.AddTestFramework(new Microsoft.Testing.Platform.MSBuild.UnitTests.SourceGeneratedTestNodesBuilder()); +#if ENABLE_CODECOVERAGE +builder.AddCodeCoverageProvider(); +#endif +builder.AddHangDumpProvider(); +builder.AddCrashDumpProvider(ignoreIfNotSupported: true); +builder.AddTrxReportProvider(); + +// Custom suite tools +CompositeExtensionFactory slowestTestCompositeServiceFactory + = new(_ => new SlowestTestsConsumer()); +builder.TestHost.AddDataConsumer(slowestTestCompositeServiceFactory); +builder.TestHost.AddTestSessionLifetimeHandle(slowestTestCompositeServiceFactory); +ITestApplication app = await builder.BuildAsync(); +return await app.RunAsync(); diff --git a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Properties/Microsoft.Testing.Platform.MSBuild.UnitTests.launcher.config.json b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Properties/Microsoft.Testing.Platform.MSBuild.UnitTests.launcher.config.json new file mode 100644 index 0000000000..d45d0ffcd0 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Properties/Microsoft.Testing.Platform.MSBuild.UnitTests.launcher.config.json @@ -0,0 +1,3 @@ +{ + "program": "Microsoft.Testing.Platform.MSBuild.UnitTests.exe" +} diff --git a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Properties/Microsoft.Testing.Platform.MSBuild.UnitTests.testingplatformconfig.json b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Properties/Microsoft.Testing.Platform.MSBuild.UnitTests.testingplatformconfig.json new file mode 100644 index 0000000000..4c41cda3a3 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Properties/Microsoft.Testing.Platform.MSBuild.UnitTests.testingplatformconfig.json @@ -0,0 +1,8 @@ +{ + "testingplatform": { + "telemetry": { + "isDevelopmentRepository": true + }, + "exitProcessOnUnhandledException": true + } +} diff --git a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Properties/launchSettings.json b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Properties/launchSettings.json new file mode 100644 index 0000000000..db50ae6ac4 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Microsoft.Testing.Platform.MSBuild.UnitTests": { + "commandName": "Project", + "commandLineArgs": "" + } + } +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Usings.cs b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Usings.cs similarity index 88% rename from test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Usings.cs rename to test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Usings.cs index 1ec60d47a9..a91def1ab6 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Usings.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Usings.cs @@ -4,5 +4,4 @@ global using Microsoft.Testing.Internal.Framework; global using Microsoft.Testing.Platform.Builder; global using Microsoft.Testing.Platform.Extensions; -global using Microsoft.Testing.Platform.Services; global using Microsoft.Testing.TestInfrastructure; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/ArgumentArityTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/ArgumentArityTests.cs index a745958610..33921a8451 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/ArgumentArityTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/ArgumentArityTests.cs @@ -1,15 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; +// Ignore Spelling: Arity using Microsoft.Testing.Platform.CommandLine; -using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.CommandLine; -using Microsoft.Testing.Platform.Extensions.OutputDevice; using Microsoft.Testing.Platform.Helpers; -using Microsoft.Testing.Platform.OutputDevice; -using Microsoft.Testing.Platform.Services; -using Microsoft.Testing.TestInfrastructure; using Moq; @@ -18,20 +13,15 @@ namespace Microsoft.Testing.Platform.UnitTests; [TestGroup] public class ArgumentArityTests : TestBase { - private readonly Mock _outputDisplayMock = new(); - private readonly Mock _testApplicationModuleInfoMock = new(); - private readonly Mock _runtimeFeatureMock = new(); - private readonly Mock _environmentMock = new(); - private readonly Mock _processHandlerMock = new(); - private readonly ICommandLineOptionsProvider[] _systemCommandLineOptionsProviders = new[] - { - new PlatformCommandLineProvider(), - }; + private readonly ICommandLineOptionsProvider[] _systemCommandLineOptionsProviders = + [ + new PlatformCommandLineProvider() + ]; - private readonly ICommandLineOptionsProvider[] _extensionCommandLineOptionsProviders = new[] - { - new ExtensionCommandLineProviderMockOptionsWithDifferentArity(), - }; + private readonly ICommandLineOptionsProvider[] _extensionCommandLineOptionsProviders = + [ + new ExtensionCommandLineProviderMockOptionsWithDifferentArity() + ]; public ArgumentArityTests(ITestExecutionContext testExecutionContext) : base(testExecutionContext) @@ -43,18 +33,14 @@ public async Task ParseAndValidate_WhenOptionWithArityZeroIsCalledWithOneArgumen // Arrange string[] args = ["--zeroArgumentsOption arg"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - _outputDisplayMock.Setup(x => x.DisplayAsync(It.IsAny(), It.IsAny())) - .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => - Assert.AreEqual($"Option '--zeroArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects no arguments{Environment.NewLine}", ((TextOutputDeviceData)data).Text, StringComparer.Ordinal)); - - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + _extensionCommandLineOptionsProviders, new Mock().Object); // Assert - Assert.IsFalse(result); + Assert.IsFalse(result.IsValid); + Assert.AreEqual("Option '--zeroArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects no arguments", result.ErrorMessage, StringComparer.Ordinal); } public async Task ParseAndValidate_WhenOptionWithArityExactlyOneIsCalledWithTwoArguments_ReturnsFalse() @@ -63,14 +49,13 @@ public async Task ParseAndValidate_WhenOptionWithArityExactlyOneIsCalledWithTwoA string[] args = ["--exactlyOneArgumentsOption arg1", "arg2"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); - // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + _extensionCommandLineOptionsProviders, new Mock().Object); // Assert - Assert.IsFalse(result); + Assert.IsFalse(result.IsValid); + Assert.AreEqual("Option '--exactlyOneArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects at most 1 arguments", result.ErrorMessage); } public async Task ParseAndValidate_WhenOptionWithArityExactlyOneIsCalledWithoutArguments_ReturnsFalse() @@ -78,18 +63,14 @@ public async Task ParseAndValidate_WhenOptionWithArityExactlyOneIsCalledWithoutA // Arrange string[] args = ["--exactlyOneArgumentsOption"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - _outputDisplayMock.Setup(x => x.DisplayAsync(It.IsAny(), It.IsAny())) - .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => - Assert.AreEqual($"Option '--exactlyOneArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects at least 1 arguments{Environment.NewLine}", ((TextOutputDeviceData)data).Text, StringComparer.Ordinal)); - - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + _extensionCommandLineOptionsProviders, new Mock().Object); // Assert - Assert.IsFalse(result); + Assert.IsFalse(result.IsValid); + Assert.AreEqual("Option '--exactlyOneArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects at least 1 arguments", result.ErrorMessage); } public async Task ParseAndValidate_WhenOptionWithArityZeroOrOneIsCalledWithTwoArguments_ReturnsFalse() @@ -97,18 +78,14 @@ public async Task ParseAndValidate_WhenOptionWithArityZeroOrOneIsCalledWithTwoAr // Arrange string[] args = ["--zeroOrOneArgumentsOption arg1", "--zeroOrOneArgumentsOption arg2"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - _outputDisplayMock.Setup(x => x.DisplayAsync(It.IsAny(), It.IsAny())) - .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => - Assert.AreEqual($"Option '--zeroOrOneArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects at most 1 arguments{Environment.NewLine}", ((TextOutputDeviceData)data).Text, StringComparer.Ordinal)); - - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + _extensionCommandLineOptionsProviders, new Mock().Object); // Assert - Assert.IsFalse(result); + Assert.IsFalse(result.IsValid); + Assert.AreEqual("Option '--zeroOrOneArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects at most 1 arguments", result.ErrorMessage); } public async Task ParseAndValidate_WhenOptionWithArityOneOrMoreIsCalledWithoutArguments_ReturnsFalse() @@ -116,18 +93,14 @@ public async Task ParseAndValidate_WhenOptionWithArityOneOrMoreIsCalledWithoutAr // Arrange string[] args = ["--oneOrMoreArgumentsOption"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - _outputDisplayMock.Setup(x => x.DisplayAsync(It.IsAny(), It.IsAny())) - .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => - Assert.AreEqual($"Option '--oneOrMoreArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects at least 1 arguments{Environment.NewLine}", ((TextOutputDeviceData)data).Text, StringComparer.Ordinal)); - - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + _extensionCommandLineOptionsProviders, new Mock().Object); // Assert - Assert.IsFalse(result); + Assert.IsFalse(result.IsValid); + Assert.AreEqual("Option '--oneOrMoreArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects at least 1 arguments", result.ErrorMessage); } public async Task ParseAndValidate_WhenOptionsGetsTheExpectedNumberOfArguments_ReturnsTrue() @@ -135,14 +108,14 @@ public async Task ParseAndValidate_WhenOptionsGetsTheExpectedNumberOfArguments_R // Arrange string[] args = ["--zeroArgumentsOption", "--zeroOrOneArgumentsOption", "--zeroOrMoreArgumentsOption arg2", "--exactlyOneArgumentsOption arg1", "oneOrMoreArgumentsOption arg1"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + _extensionCommandLineOptionsProviders, new Mock().Object); // Assert - Assert.IsTrue(result); + Assert.IsTrue(result.IsValid); + Assert.IsNull(result.ErrorMessage); } private sealed class ExtensionCommandLineProviderMockOptionsWithDifferentArity : ICommandLineOptionsProvider @@ -161,14 +134,15 @@ private sealed class ExtensionCommandLineProviderMockOptionsWithDifferentArity : /// public Task IsEnabledAsync() => Task.FromResult(true); - public IReadOnlyCollection GetCommandLineOptions() => new CommandLineOption[] - { + public IReadOnlyCollection GetCommandLineOptions() + => + [ new("zeroArgumentsOption", "Show command line zeroArgumentsOption.", ArgumentArity.Zero, false), new("zeroOrOneArgumentsOption", "Show command line zeroOrOneArgumentsOption.", ArgumentArity.ZeroOrOne, false), new("zeroOrMoreArgumentsOption", "Show command line zeroOrMoreArgumentsOption.", ArgumentArity.ZeroOrMore, false), new("exactlyOneArgumentsOption", "Show command line exactlyOneArgumentsOption.", ArgumentArity.ExactlyOne, false), - new("oneOrMoreArgumentsOption", "Show command line oneOrMoreArgumentsOption.", ArgumentArity.OneOrMore, false), - }; + new("oneOrMoreArgumentsOption", "Show command line oneOrMoreArgumentsOption.", ArgumentArity.OneOrMore, false) + ]; public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) => ValidationResult.ValidTask; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs index c5b49b8b7b..a72ccc440f 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs @@ -1,15 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.CommandLine; -using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.CommandLine; using Microsoft.Testing.Platform.Extensions.OutputDevice; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.OutputDevice; using Microsoft.Testing.Platform.Services; -using Microsoft.Testing.TestInfrastructure; using Moq; @@ -23,10 +20,10 @@ public class CommandLineHandlerTests : TestBase private readonly Mock _runtimeFeatureMock = new(); private readonly Mock _environmentMock = new(); private readonly Mock _processHandlerMock = new(); - private readonly ICommandLineOptionsProvider[] _systemCommandLineOptionsProviders = new[] - { - new PlatformCommandLineProvider(), - }; + private readonly ICommandLineOptionsProvider[] _systemCommandLineOptionsProviders = + [ + new PlatformCommandLineProvider() + ]; private readonly ICommandLineOptionsProvider[] _extensionCommandLineOptionsProviders = []; @@ -40,21 +37,15 @@ public async Task ParseAndValidateAsync_InvalidCommandLineArguments_ReturnsFalse // Arrange string[] args = ["option1", "'a'"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); - - _outputDisplayMock.Setup(x => x.DisplayAsync(It.IsAny(), It.IsAny())) - .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => - { - Assert.IsTrue(((TextOutputDeviceData)data).Text.Contains("Invalid command line arguments:")); - Assert.IsTrue(((TextOutputDeviceData)data).Text.Contains("Unexpected argument 'a'")); - }); // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + _extensionCommandLineOptionsProviders, new Mock().Object); // Assert - Assert.IsFalse(result); + Assert.IsFalse(result.IsValid); + Assert.Contains("Invalid command line arguments:", result.ErrorMessage); + Assert.Contains("Unexpected argument 'a'", result.ErrorMessage); } public async Task ParseAndValidateAsync_EmptyCommandLineArguments_ReturnsTrue() @@ -62,263 +53,239 @@ public async Task ParseAndValidateAsync_EmptyCommandLineArguments_ReturnsTrue() // Arrange string[] args = []; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + _extensionCommandLineOptionsProviders, new Mock().Object); // Assert - Assert.IsTrue(result); - _outputDisplayMock.Verify(o => o.DisplayAsync(It.IsAny(), It.IsAny()), Times.Never); - _outputDisplayMock.Verify(o => o.DisplayBannerAsync(), Times.Never); + Assert.IsTrue(result.IsValid); } - public void IsHelpInvoked_HelpOptionSet_ReturnsTrue() + public async Task ParseAndValidateAsync_DuplicateOption_ReturnsFalse() { // Arrange - string[] args = ["--help"]; + string[] args = []; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); + ICommandLineOptionsProvider[] extensionCommandLineOptionsProviders = + [ + new ExtensionCommandLineProviderMockInvalidConfiguration("userOption"), + new ExtensionCommandLineProviderMockInvalidConfiguration("userOption") + ]; // Act - bool result = commandLineHandler.IsHelpInvoked(); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + extensionCommandLineOptionsProviders, new Mock().Object); // Assert - Assert.IsTrue(result); - _outputDisplayMock.Verify(o => o.DisplayAsync(It.IsAny(), It.IsAny()), Times.Never); - _outputDisplayMock.Verify(o => o.DisplayBannerAsync(), Times.Never); + Assert.IsFalse(result.IsValid); + Assert.Contains("Option '--userOption' is declared by multiple extensions: 'userOption'", result.ErrorMessage); } - public void IsInfoInvoked_InfoOptionSet_ReturnsTrue() + public async Task ParseAndValidateAsync_InvalidOption_ReturnsFalse() { // Arrange - string[] args = ["--info"]; + string[] args = ["--diagnostic-verbosity", "r"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); // Act - bool result = commandLineHandler.IsInfoInvoked(); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + _extensionCommandLineOptionsProviders, new Mock().Object); // Assert - Assert.IsTrue(result); - _outputDisplayMock.Verify(o => o.DisplayAsync(It.IsAny(), It.IsAny()), Times.Never); - _outputDisplayMock.Verify(o => o.DisplayBannerAsync(), Times.Never); + Assert.IsFalse(result.IsValid); + Assert.AreEqual("Option '--diagnostic-verbosity' has invalid arguments: '--diagnostic-verbosity' expects a single level argument ('Trace', 'Debug', 'Information', 'Warning', 'Error', or 'Critical')", result.ErrorMessage); } - public void IsVersionInvoked_VersionOptionSet_ReturnsTrue() + public async Task ParseAndValidateAsync_InvalidArgumentArity_ReturnsFalse() { // Arrange - string[] args = ["--version"]; + string[] args = ["--help arg"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); // Act - bool result = commandLineHandler.IsOptionSet("version"); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + _extensionCommandLineOptionsProviders, new Mock().Object); // Assert - Assert.IsTrue(result); - _outputDisplayMock.Verify(o => o.DisplayAsync(It.IsAny(), It.IsAny()), Times.Never); - _outputDisplayMock.Verify(o => o.DisplayBannerAsync(), Times.Never); + Assert.IsFalse(result.IsValid); + Assert.AreEqual("Option '--help' from provider 'Platform command line provider' (UID: PlatformCommandLineProvider) expects no arguments", result.ErrorMessage); } - public void GetOptionValue_OptionExists_ReturnsOptionValue() + public async Task ParseAndValidateAsync_ReservedOptions_ReturnsFalse() { // Arrange - OptionRecord optionRecord = new("name", ["value1", "value2"]); - CommandLineHandler commandLineHandler = new([], new CommandLineParseResult(string.Empty, [optionRecord], [], []), - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); + string[] args = []; + CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); + ICommandLineOptionsProvider[] extensionCommandLineProvider = + [ + new ExtensionCommandLineProviderMockReservedOptions() + ]; // Act - bool result = commandLineHandler.TryGetOptionArgumentList("name", out string[]? optionValue); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + extensionCommandLineProvider, new Mock().Object); // Assert - Assert.IsTrue(result); - Assert.IsFalse(optionValue is null); - Assert.AreEqual(optionValue?.Length, 2); - Assert.AreEqual("value1", optionValue?[0]); - Assert.AreEqual("value2", optionValue?[1]); + Assert.IsFalse(result.IsValid); + Assert.AreEqual("Option '--help' is reserved and cannot be used by providers: 'help'", result.ErrorMessage); } - public void GetOptionValue_OptionDoesNotExist_ReturnsNull() + public async Task ParseAndValidateAsync_ReservedOptionsPrefix_ReturnsFalse() { // Arrange string[] args = []; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - - _outputDisplayMock.Setup(x => x.DisplayAsync(It.IsAny(), It.IsAny())) - .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => - { - Assert.IsTrue(((TextOutputDeviceData)data).Text.Contains("Invalid command line arguments:")); - Assert.IsTrue(((TextOutputDeviceData)data).Text.Contains("Unexpected argument")); - }); - - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); + ICommandLineOptionsProvider[] extensionCommandLineProvider = + [ + new ExtensionCommandLineProviderMockInvalidConfiguration("--internal-customextension") + ]; // Act - bool result = commandLineHandler.TryGetOptionArgumentList("name", out string[]? optionValue); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + extensionCommandLineProvider, new Mock().Object); // Assert - Assert.IsFalse(result); - Assert.IsTrue(optionValue is null); + Assert.IsFalse(result.IsValid); + Assert.AreEqual("Option `--internal-customextension` from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) is using the reserved prefix '--internal'", result.ErrorMessage); } - public async Task ParseAndValidateAsync_DuplicateOption_ReturnsFalse() + public async Task ParseAndValidateAsync_UnknownOption_ReturnsFalse() { // Arrange - string[] args = []; + string[] args = ["--x"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - _outputDisplayMock.Setup(x => x.DisplayAsync(It.IsAny(), It.IsAny())) - .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => - Assert.IsTrue(((TextOutputDeviceData)data).Text.Contains("Option '--userOption' is declared by multiple extensions: 'userOption'"))); - ICommandLineOptionsProvider[] extensionCommandLineOptionsProviders = new[] - { - new ExtensionCommandLineProviderMockInvalidConfiguration("userOption"), - new ExtensionCommandLineProviderMockInvalidConfiguration("userOption"), - }; - CommandLineHandler commandLineHandler = new(args, parseResult, - extensionCommandLineOptionsProviders, [], _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); + ICommandLineOptionsProvider[] extensionCommandLineProvider = + [ + new ExtensionCommandLineProviderMockUnknownOption() + ]; // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + extensionCommandLineProvider, new Mock().Object); // Assert - Assert.IsFalse(result); + Assert.IsFalse(result.IsValid); + Assert.AreEqual("Unknown option '--x'", result.ErrorMessage); } - public async Task ParseAndValidateAsync_InvalidOption_ReturnsFalse() + public async Task ParseAndValidateAsync_InvalidValidConfiguration_ReturnsFalse() { // Arrange - string[] args = ["--diagnostic-verbosity", "r"]; + string[] args = ["--option"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - _outputDisplayMock.Setup(x => x.DisplayAsync(It.IsAny(), It.IsAny())) - .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => - Assert.IsTrue(((TextOutputDeviceData)data).Text.Equals($"Option '--diagnostic-verbosity' has invalid arguments: '--diagnostic-verbosity' expects a single level argument ('Trace', 'Debug', 'Information', 'Warning', 'Error', or 'Critical'){Environment.NewLine}", StringComparison.Ordinal))); - - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); + ICommandLineOptionsProvider[] extensionCommandLineProvider = + [ + new ExtensionCommandLineProviderMockInvalidConfiguration() + ]; // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + ValidationResult result = await CommandLineOptionsValidator.ValidateAsync(parseResult, _systemCommandLineOptionsProviders, + extensionCommandLineProvider, new Mock().Object); // Assert - Assert.IsFalse(result); + Assert.IsFalse(result.IsValid); + Assert.AreEqual("Invalid configuration for provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider). Error: Invalid configuration errorMessage", result.ErrorMessage); } - public async Task ParseAndValidateAsync_InvalidArgumentArity_ReturnsFalse() + public void IsHelpInvoked_HelpOptionSet_ReturnsTrue() { // Arrange - string[] args = ["--help arg"]; + string[] args = ["--help"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - _outputDisplayMock.Setup(x => x.DisplayAsync(It.IsAny(), It.IsAny())) - .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => - Assert.IsTrue(((TextOutputDeviceData)data).Text.Equals($"Option '--help' from provider 'Platform command line provider' (UID: PlatformCommandLineProvider) expects no arguments{Environment.NewLine}", StringComparison.Ordinal))); - - CommandLineHandler commandLineHandler = new(args, parseResult, - _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); + CommandLineHandler commandLineHandler = new(parseResult, _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, + _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, + _processHandlerMock.Object); // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + bool result = commandLineHandler.IsHelpInvoked(); // Assert - Assert.IsFalse(result); + Assert.IsTrue(result); + _outputDisplayMock.Verify(o => o.DisplayAsync(It.IsAny(), It.IsAny()), Times.Never); + _outputDisplayMock.Verify(o => o.DisplayBannerAsync(It.IsAny()), Times.Never); } - public async Task ParseAndValidateAsync_ReservedOptions_ReturnsFalse() + public void IsInfoInvoked_InfoOptionSet_ReturnsTrue() { // Arrange - string[] args = []; + string[] args = ["--info"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - _outputDisplayMock.Setup(x => x.DisplayAsync(It.IsAny(), It.IsAny())) - .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => - Assert.IsTrue(((TextOutputDeviceData)data).Text.Equals($"Option '--help' is reserved and cannot be used by providers: 'help'{Environment.NewLine}", StringComparison.Ordinal))); - - ICommandLineOptionsProvider[] extensionCommandLineProvider = new[] - { - new ExtensionCommandLineProviderMockReservedOptions(), - }; - CommandLineHandler commandLineHandler = new(args, parseResult, extensionCommandLineProvider, - _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); + CommandLineHandler commandLineHandler = new(parseResult, _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, + _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, + _processHandlerMock.Object); // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + bool result = commandLineHandler.IsInfoInvoked(); // Assert - Assert.IsFalse(result); + Assert.IsTrue(result); + _outputDisplayMock.Verify(o => o.DisplayAsync(It.IsAny(), It.IsAny()), Times.Never); + _outputDisplayMock.Verify(o => o.DisplayBannerAsync(It.IsAny()), Times.Never); } - public async Task ParseAndValidateAsync_ReservedOptionsPrefix_ReturnsFalse() + public void IsVersionInvoked_VersionOptionSet_ReturnsTrue() { // Arrange - string[] args = []; + string[] args = ["--version"]; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - _outputDisplayMock.Setup(x => x.DisplayAsync(It.IsAny(), It.IsAny())) - .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => - Assert.IsTrue(((TextOutputDeviceData)data).Text.Equals($"Option `--internal-customextension` from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) is using the reserved prefix '--internal'{Environment.NewLine}", StringComparison.Ordinal))); - - ICommandLineOptionsProvider[] extensionCommandLineProvider = new[] - { - new ExtensionCommandLineProviderMockInvalidConfiguration("--internal-customextension"), - }; - CommandLineHandler commandLineHandler = new(args, parseResult, extensionCommandLineProvider, - _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); + CommandLineHandler commandLineHandler = new(parseResult, _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, + _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, + _processHandlerMock.Object); // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + bool result = commandLineHandler.IsOptionSet("version"); // Assert - Assert.IsFalse(result); + Assert.IsTrue(result); + _outputDisplayMock.Verify(o => o.DisplayAsync(It.IsAny(), It.IsAny()), Times.Never); + _outputDisplayMock.Verify(o => o.DisplayBannerAsync(It.IsAny()), Times.Never); } - public async Task ParseAndValidateAsync_UnknownOption_ReturnsFalse() + public void GetOptionValue_OptionExists_ReturnsOptionValue() { // Arrange - string[] args = ["--x"]; - CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); - - ICommandLineOptionsProvider[] extensionCommandLineProvider = new[] - { - new ExtensionCommandLineProviderMockUnknownOption(), - }; - CommandLineHandler commandLineHandler = new(args, parseResult, - extensionCommandLineProvider, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, - _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); + OptionRecord optionRecord = new("name", ["value1", "value2"]); + CommandLineHandler commandLineHandler = new( + new CommandLineParseResult(string.Empty, [optionRecord], [], []), _extensionCommandLineOptionsProviders, + _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, + _environmentMock.Object, _processHandlerMock.Object); // Act - (bool result, string? errorMessage) = await commandLineHandler.TryParseAndValidateAsync(); + bool result = commandLineHandler.TryGetOptionArgumentList("name", out string[]? optionValue); // Assert - Assert.IsFalse(result); - Assert.IsTrue(errorMessage!.Equals($"Unknown option '--x'{Environment.NewLine}", StringComparison.Ordinal)); + Assert.IsTrue(result); + Assert.IsFalse(optionValue is null); + Assert.AreEqual(optionValue?.Length, 2); + Assert.AreEqual("value1", optionValue?[0]); + Assert.AreEqual("value2", optionValue?[1]); } - public async Task ParseAndValidateAsync_InvalidValidConfiguration_ReturnsFalse() + public void GetOptionValue_OptionDoesNotExist_ReturnsNull() { // Arrange - string[] args = ["--option"]; + string[] args = []; CommandLineParseResult parseResult = CommandLineParser.Parse(args, new SystemEnvironment()); + _outputDisplayMock.Setup(x => x.DisplayAsync(It.IsAny(), It.IsAny())) - .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => - Assert.IsTrue(((TextOutputDeviceData)data).Text.Equals($"Invalid configuration for provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider). Error: Invalid configuration errorMessage{Environment.NewLine}{Environment.NewLine}", StringComparison.Ordinal))); + .Callback((IOutputDeviceDataProducer message, IOutputDeviceData data) => + { + Assert.IsTrue(((TextOutputDeviceData)data).Text.Contains("Invalid command line arguments:")); + Assert.IsTrue(((TextOutputDeviceData)data).Text.Contains("Unexpected argument")); + }); - ICommandLineOptionsProvider[] extensionCommandLineProvider = new[] - { - new ExtensionCommandLineProviderMockInvalidConfiguration(), - }; - CommandLineHandler commandLineHandler = new(args, parseResult, - extensionCommandLineProvider, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, - _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, _processHandlerMock.Object); + CommandLineHandler commandLineHandler = new(parseResult, _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, + _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object, _outputDisplayMock.Object, _environmentMock.Object, + _processHandlerMock.Object); // Act - (bool result, _) = await commandLineHandler.TryParseAndValidateAsync(); + bool result = commandLineHandler.TryGetOptionArgumentList("name", out string[]? optionValue); // Assert Assert.IsFalse(result); + Assert.IsTrue(optionValue is null); } private sealed class ExtensionCommandLineProviderMockReservedOptions : ICommandLineOptionsProvider @@ -339,10 +306,10 @@ private sealed class ExtensionCommandLineProviderMockReservedOptions : ICommandL /// public Task IsEnabledAsync() => Task.FromResult(true); - public IReadOnlyCollection GetCommandLineOptions() => new CommandLineOption[] - { - new(HelpOption, "Show command line help.", ArgumentArity.ZeroOrOne, false), - }; + public IReadOnlyCollection GetCommandLineOptions() => + [ + new(HelpOption, "Show command line help.", ArgumentArity.ZeroOrOne, false) + ]; public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) => throw new NotImplementedException(); @@ -367,10 +334,10 @@ private sealed class ExtensionCommandLineProviderMockUnknownOption : ICommandLin /// public Task IsEnabledAsync() => Task.FromResult(true); - public IReadOnlyCollection GetCommandLineOptions() => new CommandLineOption[] - { - new(Option, "Show command line option.", ArgumentArity.ZeroOrOne, false), - }; + public IReadOnlyCollection GetCommandLineOptions() => + [ + new(Option, "Show command line option.", ArgumentArity.ZeroOrOne, false) + ]; public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) => throw new NotImplementedException(); @@ -400,10 +367,10 @@ public ExtensionCommandLineProviderMockInvalidConfiguration(string optionName = /// public Task IsEnabledAsync() => Task.FromResult(true); - public IReadOnlyCollection GetCommandLineOptions() => new CommandLineOption[] - { - new(_option, "Show command line option.", ArgumentArity.ZeroOrOne, false), - }; + public IReadOnlyCollection GetCommandLineOptions() => + [ + new(_option, "Show command line option.", ArgumentArity.ZeroOrOne, false) + ]; public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) => ValidationResult.InvalidTask("Invalid configuration errorMessage"); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineTests.cs index 441dd3670f..1d11ea7a7d 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineTests.cs @@ -1,10 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Helpers; -using Microsoft.Testing.TestInfrastructure; namespace Microsoft.Testing.Platform.UnitTests; @@ -39,36 +37,36 @@ internal void ParserTests(int testNum, string[] args, CommandLineParseResult par internal static IEnumerable<(int TestNum, string[] Args, CommandLineParseResult ParseResult)> ParserTestsData() { - yield return (1, new string[] { "--option1", "a" }, new CommandLineParseResult(null, new List() { new("option1", ["a"]) }.ToArray(), [], [])); - yield return (2, new string[] { "--option1", "a", "b" }, new CommandLineParseResult(null, new List() { new("option1", ["a", "b"]) }.ToArray(), [], [])); - yield return (3, new string[] { "-option1", "a" }, new CommandLineParseResult(null, new List() { new("option1", ["a"]) }.ToArray(), [], [])); - yield return (4, new string[] { "--option1", "a", "-option2", "c" }, new CommandLineParseResult(null, new List() + yield return (1, ["--option1", "a"], new CommandLineParseResult(null, new List() { new("option1", ["a"]) }.ToArray(), [], [])); + yield return (2, ["--option1", "a", "b"], new CommandLineParseResult(null, new List() { new("option1", ["a", "b"]) }.ToArray(), [], [])); + yield return (3, ["-option1", "a"], new CommandLineParseResult(null, new List() { new("option1", ["a"]) }.ToArray(), [], [])); + yield return (4, ["--option1", "a", "-option2", "c"], new CommandLineParseResult(null, new List() { new("option1", ["a"]), new("option2", ["c"]), }.ToArray(), [], [])); - yield return (5, new string[] { "---option1", "a" }, new CommandLineParseResult(null, new List().ToArray(), ["Unexpected argument ---option1", "Unexpected argument a"], [])); - yield return (6, new string[] { "--option1", "'a'" }, new CommandLineParseResult(null, new List() { new("option1", ["a"]) }.ToArray(), [], [])); - yield return (7, new string[] { "--option1", "'a'", "--option2", "'hello'" }, new CommandLineParseResult(null, new List() + yield return (5, ["---option1", "a"], new CommandLineParseResult(null, new List().ToArray(), ["Unexpected argument ---option1", "Unexpected argument a"], [])); + yield return (6, ["--option1", "'a'"], new CommandLineParseResult(null, new List() { new("option1", ["a"]) }.ToArray(), [], [])); + yield return (7, ["--option1", "'a'", "--option2", "'hello'"], new CommandLineParseResult(null, new List() { new("option1", ["a"]), new("option2", ["hello"]), }.ToArray(), [], [])); - yield return (8, new string[] { "--option1", "'a'b'" }, new CommandLineParseResult(null, new List() { new("option1", []) }.ToArray(), ["Unexpected single quote in argument: 'a'b' for option option1"], [])); - yield return (9, new string[] { "option1", "--option1" }, new CommandLineParseResult("option1", new List() { new("option1", []) }.ToArray(), [], [])); - yield return (10, new string[] { "--option1", @"""\\""" }, new CommandLineParseResult(null, new List() { new("option1", ["\\"]) }.ToArray(), [], [])); - yield return (11, new string[] { "--option1", @" "" \"" "" " }, new CommandLineParseResult(null, new List() { new("option1", [" \" "]) }.ToArray(), [], [])); - yield return (12, new string[] { "--option1", @" "" \$ "" " }, new CommandLineParseResult(null, new List() { new("option1", [" $ "]) }.ToArray(), [], [])); - yield return (13, new string[] { "--option1", $@" "" \{Environment.NewLine} "" " }, new CommandLineParseResult(null, new List() { new("option1", [$" {Environment.NewLine} "]) }.ToArray(), [], [])); - yield return (14, new string[] { "--option1", "a" }, new CommandLineParseResult(null, new List() { new("option1", ["a"]) }.ToArray(), [], [])); - yield return (15, new string[] { "--option1:a" }, new CommandLineParseResult(null, new List() { new("option1", ["a"]) }.ToArray(), [], [])); - yield return (16, new string[] { "--option1=a" }, new CommandLineParseResult(null, new List() { new("option1", ["a"]) }.ToArray(), [], [])); - yield return (17, new string[] { "--option1=a", "--option1=b" }, new CommandLineParseResult(null, new List() { new("option1", ["a"]), new("option1", ["b"]) }.ToArray(), [], [])); - yield return (18, new string[] { "--option1=a", "--option1 b" }, new CommandLineParseResult(null, new List() { new("option1", ["a"]), new("option1", ["b"]) }.ToArray(), [], [])); - yield return (19, new string[] { "--option1=a=a" }, new CommandLineParseResult(null, new List() { new("option1", ["a=a"]) }.ToArray(), [], [])); - yield return (20, new string[] { "--option1=a:a" }, new CommandLineParseResult(null, new List() { new("option1", ["a:a"]) }.ToArray(), [], [])); - yield return (21, new string[] { "--option1:a=a" }, new CommandLineParseResult(null, new List() { new("option1", ["a=a"]) }.ToArray(), [], [])); - yield return (22, new string[] { "--option1:a:a" }, new CommandLineParseResult(null, new List() { new("option1", ["a:a"]) }.ToArray(), [], [])); - yield return (23, new string[] { "--option1:a:a", "--option1:a=a" }, new CommandLineParseResult(null, new List() { new("option1", ["a:a"]), new("option1", ["a=a"]) }.ToArray(), [], [])); + yield return (8, ["--option1", "'a'b'"], new CommandLineParseResult(null, new List() { new("option1", []) }.ToArray(), ["Unexpected single quote in argument: 'a'b' for option option1"], [])); + yield return (9, ["option1", "--option1"], new CommandLineParseResult("option1", new List() { new("option1", []) }.ToArray(), [], [])); + yield return (10, ["--option1", @"""\\"""], new CommandLineParseResult(null, new List() { new("option1", ["\\"]) }.ToArray(), [], [])); + yield return (11, ["--option1", @" "" \"" "" "], new CommandLineParseResult(null, new List() { new("option1", [" \" "]) }.ToArray(), [], [])); + yield return (12, ["--option1", @" "" \$ "" "], new CommandLineParseResult(null, new List() { new("option1", [" $ "]) }.ToArray(), [], [])); + yield return (13, ["--option1", $@" "" \{Environment.NewLine} "" "], new CommandLineParseResult(null, new List() { new("option1", [$" {Environment.NewLine} "]) }.ToArray(), [], [])); + yield return (14, ["--option1", "a"], new CommandLineParseResult(null, new List() { new("option1", ["a"]) }.ToArray(), [], [])); + yield return (15, ["--option1:a"], new CommandLineParseResult(null, new List() { new("option1", ["a"]) }.ToArray(), [], [])); + yield return (16, ["--option1=a"], new CommandLineParseResult(null, new List() { new("option1", ["a"]) }.ToArray(), [], [])); + yield return (17, ["--option1=a", "--option1=b"], new CommandLineParseResult(null, new List() { new("option1", ["a"]), new("option1", ["b"]) }.ToArray(), [], [])); + yield return (18, ["--option1=a", "--option1 b"], new CommandLineParseResult(null, new List() { new("option1", ["a"]), new("option1", ["b"]) }.ToArray(), [], [])); + yield return (19, ["--option1=a=a"], new CommandLineParseResult(null, new List() { new("option1", ["a=a"]) }.ToArray(), [], [])); + yield return (20, ["--option1=a:a"], new CommandLineParseResult(null, new List() { new("option1", ["a:a"]) }.ToArray(), [], [])); + yield return (21, ["--option1:a=a"], new CommandLineParseResult(null, new List() { new("option1", ["a=a"]) }.ToArray(), [], [])); + yield return (22, ["--option1:a:a"], new CommandLineParseResult(null, new List() { new("option1", ["a:a"]) }.ToArray(), [], [])); + yield return (23, ["--option1:a:a", "--option1:a=a"], new CommandLineParseResult(null, new List() { new("option1", ["a:a"]), new("option1", ["a=a"]) }.ToArray(), [], [])); } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/PlatformCommandLineProviderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/PlatformCommandLineProviderTests.cs new file mode 100644 index 0000000000..8425c9ff99 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/PlatformCommandLineProviderTests.cs @@ -0,0 +1,201 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Resources; +using Microsoft.Testing.Platform.UnitTests.Helpers; + +namespace Microsoft.Testing.Platform.UnitTests.CommandLine; + +[TestGroup] +public class PlatformCommandLineProviderTests : TestBase +{ + public PlatformCommandLineProviderTests(ITestExecutionContext testExecutionContext) + : base(testExecutionContext) + { + } + + [Arguments("Trace")] + [Arguments("Debug")] + [Arguments("Information")] + [Arguments("Warning")] + [Arguments("Error")] + [Arguments("Critical")] + public async Task IsValid_If_Verbosity_Has_CorrectValue(string dumpType) + { + var provider = new PlatformCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.DiagnosticVerbosityOptionKey); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [dumpType]).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + public async Task IsInvalid_If_Verbosity_Has_IncorrectValue() + { + var provider = new PlatformCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.DiagnosticVerbosityOptionKey); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, ["invalid"]).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(PlatformResources.PlatformCommandLineDiagnosticOptionExpectsSingleArgumentErrorMessage, validateOptionsResult.ErrorMessage); + } + + public async Task IsValid_If_Port_Is_Integer() + { + var provider = new PlatformCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.PortOptionKey); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, ["32"]).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + [Arguments("32.32")] + [Arguments("invalid")] + public async Task IsValid_If_Port_Is_Not_Integer(string port) + { + var provider = new PlatformCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.PortOptionKey); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [port]).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLinePortOptionSingleArgument, PlatformCommandLineProvider.PortOptionKey), validateOptionsResult.ErrorMessage); + } + + public async Task IsValid_If_ClientPort_Is_Integer() + { + var provider = new PlatformCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.ClientPortOptionKey); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, ["32"]).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + [Arguments("32.32")] + [Arguments("invalid")] + public async Task IsInvalid_If_ClientPort_Is_Not_Integer(string clientPort) + { + var provider = new PlatformCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.ClientPortOptionKey); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [clientPort]).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLinePortOptionSingleArgument, PlatformCommandLineProvider.ClientPortOptionKey), validateOptionsResult.ErrorMessage); + } + + public async Task IsValid_If_ExitOnProcessExit_Is_Integer() + { + var provider = new PlatformCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.ExitOnProcessExitOptionKey); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, ["32"]).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + [Arguments("32.32")] + [Arguments("invalid")] + public async Task IsInvalid_If_ExitOnProcessExit_Is_Not_Integer(string pid) + { + var provider = new PlatformCommandLineProvider(); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == PlatformCommandLineProvider.ExitOnProcessExitOptionKey); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, [pid]).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineExitOnProcessExitSingleArgument, PlatformCommandLineProvider.ExitOnProcessExitOptionKey), validateOptionsResult.ErrorMessage); + } + + public async Task IsValid_If_Diagnostics_Provided_With_Other_Diagnostics_Provided() + { + var provider = new PlatformCommandLineProvider(); + var options = new Dictionary + { + { PlatformCommandLineProvider.DiagnosticOptionKey, [] }, + { PlatformCommandLineProvider.DiagnosticOutputDirectoryOptionKey, [] }, + { PlatformCommandLineProvider.DiagnosticOutputFilePrefixOptionKey, [] }, + }; + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + public async Task IsValid_When_NoOptionSpecified() + { + var provider = new PlatformCommandLineProvider(); + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions([])).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + [Arguments(PlatformCommandLineProvider.DiagnosticOutputDirectoryOptionKey)] + [Arguments(PlatformCommandLineProvider.DiagnosticOutputFilePrefixOptionKey)] + + public async Task IsNotValid_If_Diagnostics_Missing_When_OthersDiagnostics_Provided(string optionName) + { + var provider = new PlatformCommandLineProvider(); + var options = new Dictionary + { + { optionName, [] }, + }; + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineDiagnosticOptionIsMissing, optionName), validateOptionsResult.ErrorMessage); + } + + [Arguments(true, false)] + [Arguments(false, true)] + [Arguments(false, false)] + public async Task IsValid_When_Both_DiscoverTests_MinimumExpectedTests_NotProvided(bool discoverTestsSet, bool minimumExpectedTestsSet) + { + var provider = new PlatformCommandLineProvider(); + var options = new Dictionary(); + if (discoverTestsSet) + { + options.Add(PlatformCommandLineProvider.DiscoverTestsOptionKey, []); + } + + if (minimumExpectedTestsSet) + { + options.Add(PlatformCommandLineProvider.MinimumExpectedTestsOptionKey, []); + } + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); + } + + public async Task IsInvalid_When_Both_DiscoverTests_MinimumExpectedTests_Provided() + { + var provider = new PlatformCommandLineProvider(); + var options = new Dictionary + { + { PlatformCommandLineProvider.DiscoverTestsOptionKey, [] }, + { PlatformCommandLineProvider.MinimumExpectedTestsOptionKey, [] }, + }; + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.AreEqual(PlatformResources.PlatformCommandLineMinimumExpectedTestsIncompatibleDiscoverTests, validateOptionsResult.ErrorMessage); + } + + public async Task IsNotValid_If_ExitOnProcess_Not_Running() + { + var provider = new PlatformCommandLineProvider(); + var options = new Dictionary(); + string pid = "-32"; + string[]? args = [pid]; + options.Add(PlatformCommandLineProvider.ExitOnProcessExitOptionKey, args); + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)).ConfigureAwait(false); + Assert.IsFalse(validateOptionsResult.IsValid); + Assert.IsTrue(validateOptionsResult.ErrorMessage.StartsWith($"Invalid PID '{pid}'", StringComparison.OrdinalIgnoreCase)); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/TreeNodeFilterCommandLineOptionsProviderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/TreeNodeFilterCommandLineOptionsProviderTests.cs new file mode 100644 index 0000000000..2b865e70f2 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/TreeNodeFilterCommandLineOptionsProviderTests.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.UnitTests.Helpers; + +namespace Microsoft.Testing.Platform.UnitTests.CommandLine; + +[TestGroup] +public class TreeNodeFilterCommandLineOptionsProviderTests : TestBase +{ + public TreeNodeFilterCommandLineOptionsProviderTests(ITestExecutionContext testExecutionContext) + : base(testExecutionContext) + { + } + + public async Task TreenodeFilter_AlwaysValid() + { + var provider = new TreeNodeFilterCommandLineOptionsProvider(new TestExtension()); + CommandLineOption option = provider.GetCommandLineOptions().First(x => x.Name == TreeNodeFilterCommandLineOptionsProvider.TreenodeFilter); + + ValidationResult validateOptionsResult = await provider.ValidateOptionArgumentsAsync(option, []).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + } + + public async Task CommandLineOptions_AlwaysValid() + { + var provider = new TreeNodeFilterCommandLineOptionsProvider(new TestExtension()); + + ValidationResult validateOptionsResult = await provider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions([])).ConfigureAwait(false); + Assert.IsTrue(validateOptionsResult.IsValid); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/AggregatedConfigurationTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/AggregatedConfigurationTests.cs index 0a9bb0f1b5..8c6da021b1 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/AggregatedConfigurationTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/AggregatedConfigurationTests.cs @@ -3,13 +3,11 @@ using System.Diagnostics.CodeAnalysis; -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Services; -using Microsoft.Testing.TestInfrastructure; using Moq; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationExtensionsTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationExtensionsTests.cs index a458223bc1..ead1f42c3d 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationExtensionsTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationExtensionsTests.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Configurations; -using Microsoft.Testing.TestInfrastructure; using Moq; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs index 57c9962436..4aed3e3306 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs @@ -3,13 +3,10 @@ using System.Text; -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Configurations; -using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Services; -using Microsoft.Testing.TestInfrastructure; using Moq; @@ -33,7 +30,7 @@ public async ValueTask GetConfigurationValueFromJson(string jsonFileConfig, stri Mock fileSystem = new(); fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(true); fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Open, FileAccess.Read)) - .Returns(new MemoryStream(Encoding.UTF8.GetBytes(jsonFileConfig))); + .Returns(new MemoryFileStream(Encoding.UTF8.GetBytes(jsonFileConfig))); CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(new SystemEnvironment(), new SystemProcessHandler()); ConfigurationManager configurationManager = new(fileSystem.Object, testApplicationModuleInfo); configurationManager.AddConfigurationSource(() => new JsonConfigurationSource(testApplicationModuleInfo, fileSystem.Object, null)); @@ -62,7 +59,7 @@ public async ValueTask InvalidJson_Fail() { Mock fileSystem = new(); fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(true); - fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Open)).Returns(new MemoryStream(Encoding.UTF8.GetBytes(string.Empty))); + fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Open)).Returns(new MemoryFileStream(Encoding.UTF8.GetBytes(string.Empty))); CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(new SystemEnvironment(), new SystemProcessHandler()); ConfigurationManager configurationManager = new(fileSystem.Object, testApplicationModuleInfo); configurationManager.AddConfigurationSource(() => @@ -78,9 +75,9 @@ public async ValueTask GetConfigurationValueFromJsonWithFileLoggerProvider(strin Mock fileSystem = new(); fileSystem.Setup(x => x.Exists(It.IsAny())).Returns(true); fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Open)) - .Returns(new MemoryStream(bytes)); + .Returns(new MemoryFileStream(bytes)); fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Open, FileAccess.Read)) - .Returns(new MemoryStream(bytes)); + .Returns(new MemoryFileStream(bytes)); Mock loggerMock = new(); loggerMock.Setup(x => x.IsEnabled(LogLevel.Trace)).Returns(true); @@ -136,23 +133,45 @@ public async ValueTask BuildAsync_ConfigurationSourceIsAsyncInitializableExtensi await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null)); } -} -internal class FakeConfigurationSource : IConfigurationSource, IAsyncInitializableExtension -{ - public string Uid => nameof(FakeConfigurationSource); + private class FakeConfigurationSource : IConfigurationSource, IAsyncInitializableExtension + { + public string Uid => nameof(FakeConfigurationSource); + + public string Version => "1.0.0"; - public string Version => "1.0.0"; + public string DisplayName => nameof(FakeConfigurationSource); - public string DisplayName => nameof(FakeConfigurationSource); + public string Description => nameof(FakeConfigurationSource); - public string Description => nameof(FakeConfigurationSource); + public required IConfigurationProvider ConfigurationProvider { get; set; } - public required IConfigurationProvider ConfigurationProvider { get; set; } + public IConfigurationProvider Build() => ConfigurationProvider; - public IConfigurationProvider Build() => ConfigurationProvider; + public Task InitializeAsync() => Task.CompletedTask; - public Task InitializeAsync() => Task.CompletedTask; + public Task IsEnabledAsync() => Task.FromResult(true); + } + + private class MemoryFileStream : IFileStream + { + private readonly MemoryStream _stream; - public Task IsEnabledAsync() => Task.FromResult(true); + public MemoryFileStream(byte[] bytes) + { + _stream = new MemoryStream(bytes); + } + + Stream IFileStream.Stream => _stream; + + string IFileStream.Name => string.Empty; + + void IDisposable.Dispose() + => _stream.Dispose(); + +#if NETCOREAPP + ValueTask IAsyncDisposable.DisposeAsync() + => _stream.DisposeAsync(); +#endif + } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ConsoleOutputDevice/ConsoleOutputDeviceTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ConsoleOutputDevice/ConsoleOutputDeviceTests.cs index a9db9f8bde..f4ed8281e9 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ConsoleOutputDevice/ConsoleOutputDeviceTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ConsoleOutputDevice/ConsoleOutputDeviceTests.cs @@ -3,9 +3,6 @@ using System.Globalization; -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - namespace Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice; [TestGroup] diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/CountDownEventTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/CountDownEventTests.cs index 279e07645a..832a230ce8 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/CountDownEventTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/CountDownEventTests.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Helpers; -using Microsoft.Testing.TestInfrastructure; namespace Microsoft.Testing.Platform.UnitTests; @@ -43,11 +41,7 @@ public async Task CountDownEvent_WaitAsyncCancelled_Succeeded() CountdownEvent countdownEvent = new(1); CancellationTokenSource cts = new(); var waiter = Task.Run(() => countdownEvent.WaitAsync(cts.Token)); -#if NET8_0_OR_GREATER await cts.CancelAsync(); -#else - cts.Cancel(); -#endif await Assert.ThrowsAsync(async () => await waiter); } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/SystemAsyncMonitorTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/SystemAsyncMonitorTests.cs index cd6623af67..bba20c5da7 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/SystemAsyncMonitorTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/SystemAsyncMonitorTests.cs @@ -3,9 +3,7 @@ using System.Diagnostics; -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Helpers; -using Microsoft.Testing.TestInfrastructure; namespace Microsoft.Testing.Platform.UnitTests; @@ -25,7 +23,7 @@ public async Task AsyncMonitor_ShouldCorrectlyLock() var stopwatch = Stopwatch.StartNew(); for (int i = 0; i < 3; i++) { - tasks.Add(Task.Run(() => TestLock())); + tasks.Add(Task.Run(TestLock)); } await Task.WhenAll(tasks.ToArray()); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TaskExtensionsTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TaskExtensionsTests.cs index 91ea52b310..d259a351d0 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TaskExtensionsTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TaskExtensionsTests.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Helpers; -using Microsoft.Testing.TestInfrastructure; namespace Microsoft.Testing.Platform.UnitTests; @@ -66,7 +64,7 @@ public async Task CancellationAsync_ObserveException_Succeeds() => await RetryHe async () => { ManualResetEvent waitException = new(false); - OperationCanceledException exception = await Assert.ThrowsAsync(async () + await Assert.ThrowsAsync(async () => await Task.Run(async () => { await Task.Delay(TimeSpan.FromSeconds(10)); @@ -82,7 +80,7 @@ public async Task CancellationAsyncWithReturnValue_ObserveException_Succeeds() = async () => { ManualResetEvent waitException = new(false); - OperationCanceledException exception = await Assert.ThrowsAsync(async () + await Assert.ThrowsAsync(async () => await Task.Run(async () => { await Task.Delay(TimeSpan.FromSeconds(10)); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestCommandLineOptions.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestCommandLineOptions.cs new file mode 100644 index 0000000000..2020e0fbd4 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestCommandLineOptions.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +using Microsoft.Testing.Platform.CommandLine; + +namespace Microsoft.Testing.Platform.UnitTests.Helpers; + +internal class TestCommandLineOptions : ICommandLineOptions +{ + private readonly Dictionary _options; + + public TestCommandLineOptions(Dictionary options) => _options = options; + + public bool IsOptionSet(string optionName) => _options.ContainsKey(optionName); + + public bool TryGetOptionArgumentList(string optionName, [NotNullWhen(true)] out string[]? arguments) => _options.TryGetValue(optionName, out arguments); +} diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestExtension.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestExtension.cs new file mode 100644 index 0000000000..5d2729a8ab --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestExtension.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform.UnitTests.Helpers; + +internal class TestExtension : IExtension +{ + public string Uid { get; } = "Uid"; + + public string Version { get; } = "Version"; + + public string DisplayName { get; } = "DisplayName"; + + public string Description { get; } = "Description"; + + public Task IsEnabledAsync() => Task.FromResult(true); +} diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/IPCTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/IPCTests.cs index 2459691d6f..3c86194570 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/IPCTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/IPCTests.cs @@ -1,13 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.IPC; using Microsoft.Testing.Platform.IPC.Models; using Microsoft.Testing.Platform.IPC.Serializers; using Microsoft.Testing.Platform.Logging; -using Microsoft.Testing.TestInfrastructure; using Moq; @@ -92,10 +90,10 @@ public async Task SingleConnectionNamedPipeServer_RequestReplySerialization_Succ Queue receivedMessages = new(); PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); NamedPipeClient namedPipeClient = new(pipeNameDescription.Name); - namedPipeClient.RegisterSerializer(new VoidResponseSerializer()); - namedPipeClient.RegisterSerializer(new TextMessageSerializer()); - namedPipeClient.RegisterSerializer(new IntMessageSerializer()); - namedPipeClient.RegisterSerializer(new LongMessageSerializer()); + namedPipeClient.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + namedPipeClient.RegisterSerializer(new TextMessageSerializer(), typeof(TextMessage)); + namedPipeClient.RegisterSerializer(new IntMessageSerializer(), typeof(IntMessage)); + namedPipeClient.RegisterSerializer(new LongMessageSerializer(), typeof(LongMessage)); ManualResetEventSlim manualResetEventSlim = new(false); var clientConnected = Task.Run(async () => @@ -133,10 +131,10 @@ public async Task SingleConnectionNamedPipeServer_RequestReplySerialization_Succ new Mock().Object, new SystemTask(), CancellationToken.None); - singleConnectionNamedPipeServer.RegisterSerializer(new VoidResponseSerializer()); - singleConnectionNamedPipeServer.RegisterSerializer(new TextMessageSerializer()); - singleConnectionNamedPipeServer.RegisterSerializer(new IntMessageSerializer()); - singleConnectionNamedPipeServer.RegisterSerializer(new LongMessageSerializer()); + singleConnectionNamedPipeServer.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse)); + singleConnectionNamedPipeServer.RegisterSerializer(new TextMessageSerializer(), typeof(TextMessage)); + singleConnectionNamedPipeServer.RegisterSerializer(new IntMessageSerializer(), typeof(IntMessage)); + singleConnectionNamedPipeServer.RegisterSerializer(new LongMessageSerializer(), typeof(LongMessage)); await singleConnectionNamedPipeServer.WaitConnectionAsync(CancellationToken.None); manualResetEventSlim.Wait(); @@ -244,11 +242,9 @@ private static string RandomString(int length, Random random) .Select(s => s[random.Next(s.Length)]).ToArray()); } - private abstract record class BaseMessage : IRequest - { - } + private abstract record BaseMessage : IRequest; - private sealed record class TextMessage(string Text) : BaseMessage; + private sealed record TextMessage(string Text) : BaseMessage; private sealed class TextMessageSerializer : BaseSerializer, INamedPipeSerializer { @@ -259,7 +255,7 @@ private sealed class TextMessageSerializer : BaseSerializer, INamedPipeSerialize public void Serialize(object objectToSerialize, Stream stream) => WriteString(stream, ((TextMessage)objectToSerialize).Text); } - private sealed record class IntMessage(int Integer) : BaseMessage; + private sealed record IntMessage(int Integer) : BaseMessage; private sealed class IntMessageSerializer : BaseSerializer, INamedPipeSerializer { @@ -270,7 +266,7 @@ private sealed class IntMessageSerializer : BaseSerializer, INamedPipeSerializer public void Serialize(object objectToSerialize, Stream stream) => WriteInt(stream, ((IntMessage)objectToSerialize).Integer); } - private sealed record class LongMessage(long Long) : BaseMessage; + private sealed record LongMessage(long Long) : BaseMessage; private sealed class LongMessageSerializer : BaseSerializer, INamedPipeSerializer { diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/FileLoggerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/FileLoggerTests.cs index 080def13c1..6bbc294331 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/FileLoggerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/FileLoggerTests.cs @@ -4,10 +4,8 @@ using System.Globalization; using System.Text; -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; -using Microsoft.Testing.TestInfrastructure; using Moq; @@ -81,7 +79,7 @@ public void FileLogger_NullFileSyncFlush_FileStreamCreated() .Throws() .Returns(_mockStream.Object); - string fileLoggerName = string.Empty; + string fileLoggerName; using (FileLogger fileLogger = new( new(LogFolder, LogPrefix, fileName: null, syncFlush: true), LogLevel.Trace, @@ -135,7 +133,7 @@ public void FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool syncFlus .Setup(x => x.Create(It.IsAny(), fileExists ? FileMode.Append : FileMode.CreateNew, FileAccess.Write, FileShare.Read)) .Returns(_mockStream.Object); - string fileLoggerName = string.Empty; + string fileLoggerName; using (FileLogger fileLogger = new( new(LogFolder, LogPrefix, fileName: FileName, syncFlush: syncFlush), LogLevel.Trace, @@ -162,7 +160,6 @@ public async Task Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt .Setup(x => x.Create(It.IsAny(), FileMode.CreateNew, FileAccess.Write, FileShare.Read)) .Returns(_mockStream.Object); - string fileLoggerName = string.Empty; using FileLogger fileLogger = new( new(LogFolder, LogPrefix, fileName: FileName, syncFlush: true), defaultLogLevel, @@ -197,7 +194,6 @@ public async Task Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsI .Setup(x => x.Create(It.IsAny(), FileMode.CreateNew, FileAccess.Write, FileShare.Read)) .Returns(_mockStream.Object); - string fileLoggerName = string.Empty; using FileLogger fileLogger = new( new(LogFolder, LogPrefix, fileName: FileName, syncFlush: false), defaultLogLevel, diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerFactoryTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerFactoryTests.cs index 1e61a22554..8322b1c830 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerFactoryTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerFactoryTests.cs @@ -1,10 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; -using Microsoft.Testing.TestInfrastructure; using Moq; @@ -24,10 +22,10 @@ public LoggerFactoryTests(ITestExecutionContext testExecutionContext) _mockMonitor.Setup(x => x.Lock(It.IsAny())).Returns(new Mock().Object); _mockLoggerProvider.Setup(x => x.CreateLogger(It.IsAny())).Returns(_mockLogger.Object); - _loggerProviders = new[] - { - _mockLoggerProvider.Object, - }; + _loggerProviders = + [ + _mockLoggerProvider.Object + ]; } public void LoggerFactory_LoggerCreatedOnlyOnce() @@ -41,6 +39,4 @@ public void LoggerFactory_LoggerCreatedOnlyOnce() } } -internal interface IDisposableLoggerProvider : ILoggerProvider, IDisposable -{ -} +internal interface IDisposableLoggerProvider : ILoggerProvider, IDisposable; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerTests.cs index 9aada02b15..d4be4a9dc2 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerTests.cs @@ -3,9 +3,7 @@ using System.Globalization; -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Logging; -using Microsoft.Testing.TestInfrastructure; using Moq; @@ -33,7 +31,7 @@ private Logger CreateLogger(LogLevel logLevel) { _mockLogger.Setup(x => x.IsEnabled(It.IsAny())).Returns(currentLogLevel => currentLogLevel >= logLevel); - Logger logger = new(new[] { _mockLogger.Object }, logLevel); + Logger logger = new([_mockLogger.Object], logLevel); Mock mockLoggerFactory = new(); mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny())).Returns(logger); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggingExtensionsTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggingExtensionsTests.cs index 71318090e0..ac6b5d61bc 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggingExtensionsTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggingExtensionsTests.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Logging; -using Microsoft.Testing.TestInfrastructure; using Moq; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/NopLoggerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/NopLoggerTests.cs index e4a71a7933..1db2c78371 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/NopLoggerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/NopLoggerTests.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Logging; -using Microsoft.Testing.TestInfrastructure; namespace Microsoft.Testing.Platform.UnitTests; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/ServerLoggerForwarderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/ServerLoggerForwarderTests.cs index 07eee267f3..10be06a371 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/ServerLoggerForwarderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/ServerLoggerForwarderTests.cs @@ -3,13 +3,11 @@ using System.Globalization; -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Hosts; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Services; -using Microsoft.Testing.TestInfrastructure; using Moq; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.cs index ec7ce29b5d..761f09c1c1 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.cs @@ -1,14 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Messages; using Microsoft.Testing.Platform.Services; -using Microsoft.Testing.TestInfrastructure; namespace Microsoft.Testing.Platform.UnitTests; @@ -36,7 +34,7 @@ public async Task UnexpectedTypePublished_ShouldFail() // Fire consume with a good message await proxy.PublishAsync(new DummyProducer("DummyProducer", typeof(InvalidTypePublished.ValidDataToProduce)), new InvalidTypePublished.ValidDataToProduce()); consumer.Published.WaitOne(TimeoutHelper.DefaultHangTimeoutMilliseconds); - await Assert.ThrowsAsync(() => proxy.DrainDataAsync()); + await Assert.ThrowsAsync(proxy.DrainDataAsync); } public async Task DrainDataAsync_Loop_ShouldFail() @@ -163,7 +161,7 @@ public DummyConsumer(Func action) public List DummyDataList { get; } = []; - public Type[] DataTypesConsumed => new[] { typeof(DummyData) }; + public Type[] DataTypesConsumed => [typeof(DummyData)]; public string Uid => nameof(DummyConsumer); @@ -197,7 +195,7 @@ public sealed class DummyData : IData public sealed class DummyProducer : IDataProducer { - public Type[] DataTypesProduced => new[] { typeof(DummyData) }; + public Type[] DataTypesProduced => [typeof(DummyData)]; public string Uid => nameof(DummyProducer); @@ -228,7 +226,7 @@ public LoopConsumerA(IMessageBus messageBus) _messageBus = messageBus; } - public Type[] DataTypesConsumed => new[] { typeof(LoopDataB) }; + public Type[] DataTypesConsumed => [typeof(LoopDataB)]; public string Uid => nameof(LoopConsumerA); @@ -238,7 +236,7 @@ public LoopConsumerA(IMessageBus messageBus) public string Description => string.Empty; - public Type[] DataTypesProduced => new[] { typeof(LoopDataA) }; + public Type[] DataTypesProduced => [typeof(LoopDataA)]; public Task IsEnabledAsync() => Task.FromResult(true); @@ -282,7 +280,7 @@ public ConsumerB(IMessageBus messageBus) _messageBus = messageBus; } - public Type[] DataTypesConsumed => new[] { typeof(LoopDataA) }; + public Type[] DataTypesConsumed => [typeof(LoopDataA)]; public string Uid => nameof(LoopConsumerA); @@ -292,7 +290,7 @@ public ConsumerB(IMessageBus messageBus) public string Description => string.Empty; - public Type[] DataTypesProduced => new[] { typeof(LoopDataB) }; + public Type[] DataTypesProduced => [typeof(LoopDataB)]; public Task IsEnabledAsync() => Task.FromResult(true); @@ -321,7 +319,7 @@ public Consumer(IMessageBus messageBus, string id) public List ConsumedData { get; } = []; - public Type[] DataTypesConsumed => new[] { typeof(Data) }; + public Type[] DataTypesConsumed => [typeof(Data)]; public string Uid { get; set; } @@ -331,7 +329,7 @@ public Consumer(IMessageBus messageBus, string id) public string Description => string.Empty; - public Type[] DataTypesProduced => new[] { typeof(Data) }; + public Type[] DataTypesProduced => [typeof(Data)]; public Task IsEnabledAsync() => Task.FromResult(true); @@ -354,11 +352,11 @@ public InvalidTypePublished(IMessageBus messageBus) public ManualResetEvent Published { get; set; } = new(false); - public Type[] DataTypesConsumed => new[] { typeof(ValidDataToProduce) }; + public Type[] DataTypesConsumed => [typeof(ValidDataToProduce)]; public string Uid => nameof(InvalidTypePublished); - public Type[] DataTypesProduced => new[] { typeof(ValidDataToProduce) }; + public Type[] DataTypesProduced => [typeof(ValidDataToProduce)]; public string Version => "1.0.0"; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/PropertyBagTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/PropertyBagTests.cs index 6c7b98f19d..0dfb5466ae 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/PropertyBagTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/PropertyBagTests.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Extensions.Messages; -using Microsoft.Testing.TestInfrastructure; namespace Microsoft.Testing.Platform.UnitTests; @@ -164,11 +162,7 @@ public void EmptyProperties_Should_NotFail() } } - private sealed class DummyProperty : IProperty - { - } + private sealed class DummyProperty : IProperty; - private sealed class DummyProperty2 : IProperty - { - } + private sealed class DummyProperty2 : IProperty; } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodeUidTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodeUidTests.cs index 6ea20f7feb..5670d209cc 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodeUidTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodeUidTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - using FrameworkTestNodeUid = Microsoft.Testing.Internal.Framework.TestNodeUid; using TestNodeUid = Microsoft.Testing.Platform.Extensions.Messages.TestNodeUid; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Microsoft.Testing.Platform.UnitTests.csproj b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Microsoft.Testing.Platform.UnitTests.csproj index dc670c7eee..6dc5d8f9fc 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Microsoft.Testing.Platform.UnitTests.csproj +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Microsoft.Testing.Platform.UnitTests.csproj @@ -1,17 +1,8 @@ - + $(MicrosoftTestingTargetFrameworks);net462 - Exe - false - false - win-x64 - false false - true - enable - true - false @@ -21,11 +12,6 @@ - - - - - @@ -35,19 +21,6 @@ - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Program.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Program.cs index fdbe0aff05..3c5b97f952 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Program.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Program.cs @@ -4,27 +4,20 @@ using System.Diagnostics; using Microsoft.Testing.Extensions; -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Internal.Framework.Configurations; -using Microsoft.Testing.Platform.Builder; -using Microsoft.Testing.Platform.CommandLine; -using Microsoft.Testing.Platform.Extensions; -using Microsoft.Testing.Platform.Extensions.TestHost; -using Microsoft.Testing.Platform.Services; -using Microsoft.Testing.TestInfrastructure; // Opt-out telemetry Environment.SetEnvironmentVariable("DOTNET_CLI_TELEMETRY_OPTOUT", "1"); // DebuggerUtility.AttachVSToCurrentProcess(); ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.TestHost.AddTestApplicationLifecycleCallbacks(sp => new GlobalTasks(sp.GetCommandLineOptions())); builder.AddTestFramework(new TestFrameworkConfiguration(Debugger.IsAttached ? 1 : Environment.ProcessorCount), new Microsoft.Testing.Platform.UnitTests.SourceGeneratedTestNodesBuilder()); #if ENABLE_CODECOVERAGE builder.AddCodeCoverageProvider(); #endif builder.AddCrashDumpProvider(); +builder.AddHangDumpProvider(); builder.AddTrxReportProvider(); // Custom suite tools @@ -33,36 +26,3 @@ builder.TestHost.AddTestSessionLifetimeHandle(slowestTestCompositeServiceFactory); ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); - -internal sealed class GlobalTasks : ITestApplicationLifecycleCallbacks -{ - private readonly ICommandLineOptions _commandLineOptions; - - public GlobalTasks(ICommandLineOptions commandLineOptions) - { - _commandLineOptions = commandLineOptions; - } - - public string Uid => nameof(GlobalTasks); - - public string Version => "1.0.0"; - - public string DisplayName => string.Empty; - - public string Description => string.Empty; - - public Task IsEnabledAsync() => Task.FromResult(true); - - public async Task AfterRunAsync(int returnValue, CancellationToken cancellationToken) - { - // Check if any tests are missing that were supposed to run. -#if NETCOREAPP - TestsRunWatchDog.BaselineFile = Path.Combine(AppContext.BaseDirectory, "testsbaseline.txt"); -#else - TestsRunWatchDog.BaselineFile = Path.Combine(AppContext.BaseDirectory, "testsbaseline.netfx.txt"); -#endif - await TestsRunWatchDog.VerifyAsync(skip: _commandLineOptions.IsServerMode(), fixBaseLine: true); - } - - public Task BeforeRunAsync(CancellationToken cancellationToken) => Task.CompletedTask; -} diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Requests/TreeNodeFilterTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Requests/TreeNodeFilterTests.cs index d553c4123a..49523583b0 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Requests/TreeNodeFilterTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Requests/TreeNodeFilterTests.cs @@ -1,11 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Requests; -using Microsoft.Testing.TestInfrastructure; +#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. namespace Microsoft.Testing.Platform.UnitTests; [TestGroup] diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs index 2cfec32fbf..99cc201be8 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs @@ -1,10 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.ServerMode; -using Microsoft.Testing.TestInfrastructure; using TestNode = Microsoft.Testing.Platform.Extensions.Messages.TestNode; using TestNodeUid = Microsoft.Testing.Platform.Extensions.Messages.TestNodeUid; @@ -226,7 +224,7 @@ private static void AssertSerialize(Type type, string instanceSerialized) if (type == typeof(InitializeResponseArgs)) { - Assert.AreEqual("""{"serverInfo":{"name":"ServerInfoName","version":"Version"},"capabilities":{"testing":{"supportsDiscovery":true,"experimental_multiRequestSupport":true,"vstestProvider":true}}}""".Replace(" ", string.Empty), instanceSerialized, because); + Assert.AreEqual("""{"processId":1,"serverInfo":{"name":"ServerInfoName","version":"Version"},"capabilities":{"testing":{"supportsDiscovery":true,"experimental_multiRequestSupport":true,"vstestProvider":true}}}""".Replace(" ", string.Empty), instanceSerialized, because); return; } @@ -362,7 +360,7 @@ private static object CreateInstance(Type type) if (type == typeof(InitializeResponseArgs)) { - return new InitializeResponseArgs(new ServerInfo("ServerInfoName", "Version"), new ServerCapabilities(new ServerTestingCapabilities(true, true, true))); + return new InitializeResponseArgs(1, new ServerInfo("ServerInfoName", "Version"), new ServerCapabilities(new ServerTestingCapabilities(true, true, true))); } if (type == typeof(ErrorMessage)) @@ -389,15 +387,14 @@ private static object CreateInstance(Type type) { return new DiscoverRequestArgs( Guid.Empty, - new TestNode[] - { + [ new() { Uid = new TestNodeUid("UnitTest1.TestMethod1"), DisplayName = "test1", Properties = new PropertyBag(new TestFileLocationProperty("filePath", new LinePositionSpan(new(1, 0), new(2, 0)))), - }, - }, + } + ], null); } @@ -405,15 +402,14 @@ private static object CreateInstance(Type type) { return new RunRequestArgs( Guid.Empty, - new TestNode[] - { + [ new() { Uid = new TestNodeUid("UnitTest1.TestMethod1"), DisplayName = "test1", Properties = new PropertyBag(new TestFileLocationProperty("filePath", new LinePositionSpan(new(1, 0), new(2, 0)))), - }, - }, + } + ], null); } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsonTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsonTests.cs index 10deb99436..bae1ad7d57 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsonTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsonTests.cs @@ -1,11 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.ServerMode; using Microsoft.Testing.Platform.ServerMode.Json; -using Microsoft.Testing.TestInfrastructure; using TestNode = Microsoft.Testing.Platform.Extensions.Messages.TestNode; @@ -87,11 +85,11 @@ public async Task Serialize_ArrayOfObjects() Dictionary converters = new() { [typeof(Person)] = new JsonObjectSerializer( - n => new (string Key, object? Value)[] - { + n => + [ ("name", n.Name), - ("children", n.Children), - }), + ("children", n.Children) + ]), }; Person person = new() @@ -126,7 +124,7 @@ public void DeserializePerson() Children = json.Bind>(jsonElement, "children"), }), - [typeof(List)] = new JsonCollectionDeserializer, Person>(_ => new List(), (c, i) => c.Add(i)), + [typeof(List)] = new JsonCollectionDeserializer, Person>(_ => [], (c, i) => c.Add(i)), }); // Act @@ -150,7 +148,7 @@ public void DeserializePersonList() Children = json.Bind>(jsonElement, "children"), }), - [typeof(List)] = new JsonCollectionDeserializer, Person>(_ => new List(), (c, i) => c.Add(i)), + [typeof(List)] = new JsonCollectionDeserializer, Person>(_ => [], (c, i) => c.Add(i)), }); // Act diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsoniteTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsoniteTests.cs index 1f61866c0f..cee4678ebf 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsoniteTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsoniteTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.TestInfrastructure; - namespace Microsoft.Testing.Platform.UnitTests; [TestGroup] diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerDataConsumerServiceTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerDataConsumerServiceTests.cs index 8738cc1cb5..71054ee0d7 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerDataConsumerServiceTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerDataConsumerServiceTests.cs @@ -1,14 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Hosts; using Microsoft.Testing.Platform.ServerMode; using Microsoft.Testing.Platform.Services; using Microsoft.Testing.Platform.TestHost; -using Microsoft.Testing.TestInfrastructure; using Moq; @@ -42,10 +40,10 @@ public async Task ConsumeAsync_WithSessionFileArtifact() await _service.ConsumeAsync(producer, sessionFileArtifact, CancellationToken.None).ConfigureAwait(false); List actual = _service.Artifacts; - List expected = new() - { - new("file", nameof(DataProducer), "file", "name", "description"), - }; + List expected = + [ + new("file", nameof(DataProducer), "file", "name", "description") + ]; Uri actualUri = new(actual[0].Uri); @@ -200,7 +198,7 @@ private sealed class DataProducer : IDataProducer public string Description => string.Empty; - public Type[] DataTypesProduced => new[] { typeof(SessionFileArtifact) }; + public Type[] DataTypesProduced => [typeof(SessionFileArtifact)]; public Task IsEnabledAsync() => Task.FromResult(true); } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerTests.cs index 202cdceb3d..4ec3befa54 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerTests.cs @@ -5,8 +5,6 @@ using System.Net.Sockets; using System.Text; -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.Capabilities; using Microsoft.Testing.Platform.Capabilities.TestFramework; using Microsoft.Testing.Platform.Extensions.TestFramework; @@ -14,7 +12,6 @@ using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.ServerMode; using Microsoft.Testing.Platform.Services; -using Microsoft.Testing.TestInfrastructure; namespace Microsoft.Testing.Platform.UnitTests; @@ -65,7 +62,7 @@ public async Task ServerCanInitialize() builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new MockTestAdapter()); var testApplication = (TestApplication)await builder.BuildAsync(); testApplication.ServiceProvider.GetRequiredService().SuppressOutput(); - var serverTask = Task.Run(() => testApplication.RunAsync()); + var serverTask = Task.Run(testApplication.RunAsync); using CancellationTokenSource timeout = new(TimeoutHelper.DefaultHangTimeSpanTimeout); using TcpClient client = await server.WaitForConnectionAsync(timeout.Token); @@ -115,6 +112,7 @@ await WriteMessageAsync( InitializeResponseArgs resultJson = SerializerUtilities.Deserialize((IDictionary)((ResponseMessage)msg).Result!); InitializeResponseArgs expectedResponse = new( + 1, new ServerInfo("test-anywhere", "this is dynamic"), new ServerCapabilities(new ServerTestingCapabilities(SupportsDiscovery: true, MultiRequestSupport: false, VSTestProviderSupport: false))); @@ -149,7 +147,7 @@ public async Task DiscoveryRequestCanBeCanceled() }); var testApplication = (TestApplication)await builder.BuildAsync(); testApplication.ServiceProvider.GetRequiredService().SuppressOutput(); - var serverTask = Task.Run(() => testApplication.RunAsync()); + var serverTask = Task.Run(testApplication.RunAsync); using CancellationTokenSource timeout = new(TimeoutHelper.DefaultHangTimeSpanTimeout); using TcpClient client = await server.WaitForConnectionAsync(timeout.Token); @@ -282,7 +280,7 @@ private sealed class MockTestAdapter : ITestFramework { public Func? DiscoveryAction { get; set; } - public ICapability[] Capabilities => Array.Empty(); + public ICapability[] Capabilities => []; public string Uid => nameof(MockTestAdapter); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ServiceProviderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ServiceProviderTests.cs index 4b84c51dfe..2053169c0d 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ServiceProviderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ServiceProviderTests.cs @@ -1,16 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Capabilities; -using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestFramework; using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Extensions.TestHostControllers; using Microsoft.Testing.Platform.Services; using Microsoft.Testing.Platform.TestHost; -using Microsoft.Testing.TestInfrastructure; namespace Microsoft.Testing.Platform.UnitTests; @@ -124,15 +121,15 @@ public void AddService_SameInstance_ShouldNotFail() public void AddServices_SameInstance_ShouldFail() { TestHostProcessLifetimeHandler instance = new(); - _serviceProvider.AddServices(new[] { instance }); - _ = Assert.Throws(() => _serviceProvider.AddServices(new[] { instance })); + _serviceProvider.AddServices([instance]); + _ = Assert.Throws(() => _serviceProvider.AddServices([instance])); } public void AddServices_SameInstance_ShouldNotFail() { TestHostProcessLifetimeHandler instance = new(); - _serviceProvider.AddServices(new[] { instance }); - _serviceProvider.AddServices(new[] { instance }, throwIfSameInstanceExit: false); + _serviceProvider.AddServices([instance]); + _serviceProvider.AddServices([instance], throwIfSameInstanceExit: false); } public void TryAddService_SameInstance_ShouldReturnFalse() diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/TestApplicationResultTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/TestApplicationResultTests.cs index 47a61a546a..2e8066def4 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/TestApplicationResultTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/TestApplicationResultTests.cs @@ -1,13 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.OutputDevice; using Microsoft.Testing.Platform.Services; -using Microsoft.Testing.TestInfrastructure; using Moq; @@ -24,7 +22,7 @@ public TestApplicationResultTests(ITestExecutionContext testExecutionContext) { } - public async Task GetProcessExitCodeAsync_If_All_Skipped_Returns_Zero() + public async Task GetProcessExitCodeAsync_If_All_Skipped_Returns_ZeroTestsRan() { await _testApplicationResult.ConsumeAsync(new DummyProducer(), new TestNodeUpdateMessage( default, @@ -35,7 +33,7 @@ public async Task GetProcessExitCodeAsync_If_All_Skipped_Returns_Zero() Properties = new PropertyBag(SkippedTestNodeStateProperty.CachedInstance), }), CancellationToken.None); - Assert.AreEqual(ExitCodes.Success, await _testApplicationResult.GetProcessExitCodeAsync()); + Assert.AreEqual(ExitCodes.ZeroTests, await _testApplicationResult.GetProcessExitCodeAsync()); } public async Task GetProcessExitCodeAsync_If_No_Tests_Ran_Returns_ZeroTestsRan() @@ -191,7 +189,7 @@ public async Task GetProcessExitCodeAsync_IgnoreExitCodes(string argument, int e foreach (TestApplicationResult testApplicationResult in new TestApplicationResult[] { new(new Mock().Object, new Mock().Object, - new CommandLineOption(PlatformCommandLineProvider.IgnoreExitCodeOptionKey, argument is null ? Array.Empty() : new[] { argument }), + new CommandLineOption(PlatformCommandLineProvider.IgnoreExitCodeOptionKey, argument is null ? [] : [argument]), new Mock().Object), new(new Mock().Object, new Mock().Object, new Mock().Object, diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/ServerTelemetryTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/ServerTelemetryTests.cs index a634a5372a..524b03589c 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/ServerTelemetryTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/ServerTelemetryTests.cs @@ -1,11 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Hosts; using Microsoft.Testing.Platform.ServerMode; using Microsoft.Testing.Platform.Telemetry; -using Microsoft.Testing.TestInfrastructure; using Moq; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs index 8e04e3be1c..90fc478266 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; -using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions.OutputDevice; using Microsoft.Testing.Platform.Helpers; @@ -10,7 +8,6 @@ using Microsoft.Testing.Platform.OutputDevice; using Microsoft.Testing.Platform.Services; using Microsoft.Testing.Platform.Telemetry; -using Microsoft.Testing.TestInfrastructure; using Moq; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/TestApplicationBuilderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/TestApplicationBuilderTests.cs index 1d37dab6a6..2ea9056087 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/TestApplicationBuilderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/TestApplicationBuilderTests.cs @@ -3,9 +3,7 @@ using System.Diagnostics.CodeAnalysis; -using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Configurations; -using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Extensions.TestHostControllers; @@ -13,7 +11,6 @@ using Microsoft.Testing.Platform.Services; using Microsoft.Testing.Platform.TestHost; using Microsoft.Testing.Platform.TestHostControllers; -using Microsoft.Testing.TestInfrastructure; namespace Microsoft.Testing.Platform.UnitTests; @@ -26,7 +23,7 @@ public TestApplicationBuilderTests(ITestExecutionContext testExecutionContext) : base(testExecutionContext) { CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(new SystemEnvironment(), new SystemProcessHandler()); - AggregatedConfiguration configuration = new(Array.Empty(), testApplicationModuleInfo, new SystemFileSystem()); + AggregatedConfiguration configuration = new([], testApplicationModuleInfo, new SystemFileSystem()); configuration.SetCurrentWorkingDirectory(string.Empty); configuration.SetCurrentWorkingDirectory(string.Empty); _serviceProvider.AddService(configuration); @@ -90,7 +87,7 @@ public async Task TestHost_ComposeFactory_ShouldSucceed(bool withParameter) : new(() => new TestSessionLifetimeHandlerPlusConsumer()); testHostManager.AddTestSessionLifetimeHandle(compositeExtensionFactory); testHostManager.AddDataConsumer(compositeExtensionFactory); - List compositeExtensions = new(); + List compositeExtensions = []; IDataConsumer[] consumers = (await testHostManager.BuildDataConsumersAsync(_serviceProvider, compositeExtensions)).Select(x => (IDataConsumer)x.Consumer).ToArray(); ITestSessionLifetimeHandler[] sessionLifetimeHandle = (await testHostManager.BuildTestSessionLifetimeHandleAsync(_serviceProvider, compositeExtensions)).Select(x => (ITestSessionLifetimeHandler)x.TestSessionLifetimeHandler).ToArray(); Assert.AreEqual(1, consumers.Length); @@ -148,7 +145,6 @@ public async Task TestHostController_ComposeFactory_ShouldSucceed(bool withParam : new(() => new TestHostProcessLifetimeHandlerPlusTestHostEnvironmentVariableProvider()); testHostControllerManager.AddEnvironmentVariableProvider(compositeExtensionFactory); testHostControllerManager.AddProcessLifetimeHandler(compositeExtensionFactory); - List compositeExtensions = new(); TestHostControllerConfiguration configuration = await testHostControllerManager.BuildAsync(_serviceProvider); Assert.IsTrue(configuration.RequireProcessRestart); Assert.AreEqual(1, configuration.LifetimeHandlers.Length); @@ -306,7 +302,7 @@ public TestSessionLifetimeHandlerPlusConsumer(IServiceProvider serviceProvider) public string Description => nameof(TestSessionLifetimeHandlerPlusConsumer); - public Type[] DataTypesConsumed => Array.Empty(); + public Type[] DataTypesConsumed => []; public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) => throw new NotImplementedException(); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/testsbaseline.netfx.txt b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/testsbaseline.netfx.txt deleted file mode 100644 index 639d1c8a8e..0000000000 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/testsbaseline.netfx.txt +++ /dev/null @@ -1,353 +0,0 @@ -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.CheckTestResultsDirectoryOverrideAndCreateItAsync_ResultsDirectoryIsNull_GetDefaultDirectory() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.CheckTestResultsDirectoryOverrideAndCreateItAsync_ResultsDirectoryIsNull_GetDirectoryFromCommandLineProvider() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.CheckTestResultsDirectoryOverrideAndCreateItAsync_ResultsDirectoryIsNull_GetDirectoryFromStore() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_CurrentWorkingDirectorySet_DirectoryIsNotNull() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetAndNoConfigurationProviders_DirectoryIsNull(string) (key: "testingPlatform:currentWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetAndNoConfigurationProviders_DirectoryIsNull(string) (key: "testingPlatform:resultDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetAndNoConfigurationProviders_DirectoryIsNull(string) (key: "testingPlatform:testHostWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNotNull(string) (key: "testingPlatform:currentWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNotNull(string) (key: "testingPlatform:resultDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNotNull(string) (key: "testingPlatform:testHostWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNull(string) (key: "testingPlatform:currentWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNull(string) (key: "testingPlatform:resultDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNull(string) (key: "testingPlatform:testHostWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_ResultDirectorySet_DirectoryIsNotNull() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_TestHostWorkingDirectorySet_DirectoryIsNotNull() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ArgumentArityTests.ParseAndValidate_WhenOptionsGetsTheExpectedNumberOfArguments_ReturnsTrue() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ArgumentArityTests.ParseAndValidate_WhenOptionWithArityExactlyOneIsCalledWithoutArguments_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ArgumentArityTests.ParseAndValidate_WhenOptionWithArityExactlyOneIsCalledWithTwoArguments_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ArgumentArityTests.ParseAndValidate_WhenOptionWithArityOneOrMoreIsCalledWithoutArguments_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ArgumentArityTests.ParseAndValidate_WhenOptionWithArityZeroIsCalledWithOneArgument_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ArgumentArityTests.ParseAndValidate_WhenOptionWithArityZeroOrOneIsCalledWithTwoArguments_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AsynchronousMessageBusTests.Consumers_ConsumeData_ShouldNotMissAnyPayload() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AsynchronousMessageBusTests.DrainDataAsync_Loop_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AsynchronousMessageBusTests.MessageBus_WhenConsumerProducesAndConsumesTheSameType_ShouldNotConsumeWhatProducedByItself() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AsynchronousMessageBusTests.UnexpectedTypePublished_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.GetOptionValue_OptionDoesNotExist_ReturnsNull() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.GetOptionValue_OptionExists_ReturnsOptionValue() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.IsHelpInvoked_HelpOptionSet_ReturnsTrue() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.IsInfoInvoked_InfoOptionSet_ReturnsTrue() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.IsVersionInvoked_VersionOptionSet_ReturnsTrue() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_DuplicateOption_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_EmptyCommandLineArguments_ReturnsTrue() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_InvalidArgumentArity_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_InvalidCommandLineArguments_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_InvalidOption_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_InvalidValidConfiguration_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_ReservedOptions_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_ReservedOptionsPrefix_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_UnknownOption_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationExtensionsTests.ConfigurationExtensions_TestedMethod_ReturnsExpectedPath(string) (key: "testingPlatform:currentWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationExtensionsTests.ConfigurationExtensions_TestedMethod_ReturnsExpectedPath(string) (key: "testingPlatform:resultDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationExtensionsTests.ConfigurationExtensions_TestedMethod_ReturnsExpectedPath(string) (key: "testingPlatform:testHostWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationExtensionsTests.ConfigurationExtensions_TestedMethod_ThrowsArgumentNullException(string) (key: "testingPlatform:currentWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationExtensionsTests.ConfigurationExtensions_TestedMethod_ThrowsArgumentNullException(string) (key: "testingPlatform:resultDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationExtensionsTests.ConfigurationExtensions_TestedMethod_ThrowsArgumentNullException(string) (key: "testingPlatform:testHostWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.BuildAsync_ConfigurationSourceIsAsyncInitializableExtension_InitializeAsyncIsCalled() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.BuildAsync_ConfigurationSourcesNotEnabledAsync_ThrowsException() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.BuildAsync_EmptyConfigurationSources_ThrowsException() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [10] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [11] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [12] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [7] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [8] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [9] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [10] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [11] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [12] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [7] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [8] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [9] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.InvalidJson_Fail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDuration_InvalidInput_ShouldReturnNull(double?) (durationInMs: -1) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDuration_InvalidInput_ShouldReturnNull(double?) (durationInMs: null) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "0ms", time: "00 00 00 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "100ms", time: "00 00 00 100") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "10h 00m 00s 000ms", time: "10 00 00 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "10ms", time: "00 00 00 010") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "11m 00s 000ms", time: "00 11 00 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "11m 01s 100ms", time: "00 11 01 100") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "11s 100ms", time: "00 00 11 100") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "1d 01h 00m 00s 000ms", time: "25 00 00 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "1m 00s 000ms", time: "00 01 00 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "1ms", time: "00 00 00 001") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "1s 000ms", time: "00 00 01 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "1s 100ms", time: "00 00 01 100") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "2d 01h 00m 00s 000ms", time: "49 00 00 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "59m 01s 100ms", time: "00 59 01 100") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CountDownEventTests.CountDownEvent_WaitAsync_Succeeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CountDownEventTests.CountDownEvent_WaitAsyncCancelled_Succeeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CountDownEventTests.CountDownEvent_WaitAsyncCancelledByTimeout_Succeeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.FileLogger_NullFileSyncFlush_FileStreamCreated() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.FileLogger_NullFileSyncFlush_FileStreamCreationThrows() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool, bool) (syncFlush: false, fileExists: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool, bool) (syncFlush: false, fileExists: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool, bool) (syncFlush: true, fileExists: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool, bool) (syncFlush: true, fileExists: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Write_IfMalformedUTF8_ShouldNotCrash() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.CanDeserializeTaskResponse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.DeserializeSpecificTypes(System.Type) (type: typeof(Microsoft.Testing.Platform.ServerMode.DiscoverRequestArgs)) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.DeserializeSpecificTypes(System.Type) (type: typeof(Microsoft.Testing.Platform.ServerMode.RunRequestArgs)) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (Artifact) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (AttachDebuggerInfoArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (CancelRequestArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (DiscoverResponseArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (ErrorMessage) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (InitializeResponseArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (KeyValuePair`2) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (LogEventArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (NotificationMessage) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (Object) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (ProcessInfoArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (RequestMessage) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (ResponseMessage) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (RunResponseArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (ServerCapabilities) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (ServerInfo) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (ServerTestingCapabilities) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (TelemetryEventArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (TestNode) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (TestNodeStateChangedEventArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (TestNodeUpdateMessage) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.IPCTests.ConnectionNamedPipeServer_MultipleConnection_Succeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.IPCTests.SingleConnectionNamedPipeServer_MultipleConnection_Fails() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.IPCTests.SingleConnectionNamedPipeServer_RequestReplySerialization_Succeeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.JsoniteTests.Serialize_DateTimeOffset() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerFactoryTests.LoggerFactory_LoggerCreatedOnlyOnce() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogCritical_CallsLogWithLogLevelCritical() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogCriticalAsync_CallsLogAsyncWithLogLevelCritical() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogDebug_CallsLogWithLogLevelDebug() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogDebugAsync_CallsLogAsyncWithLogLevelDebug() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogError_CallsLogWithLogLevelError() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogError_CallsLogWithLogLevelErrorAndException() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogError_CallsLogWithLogLevelErrorAndMessageAndException() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogErrorAsync_CallsLogAsyncWithLogLevelError() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogErrorAsync_CallsLogAsyncWithLogLevelErrorAndException() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogErrorAsync_CallsLogAsyncWithLogLevelErrorAndMessageAndException() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogInformation_CallsLogWithLogLevelInformation() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogInformationAsync_CallsLogAsyncWithLogLevelInformation() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogTrace_CallsLogWithLogLevelTrace() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogTraceAsync_CallsLogAsyncWithLogLevelTrace() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogWarning_CallsLogWithLogLevelWarning() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogWarningAsync_CallsLogAsyncWithLogLevelWarning() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Add_Of_TestNodeStateProperty_More_Than_One_Time_Fail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Add_Same_Instance_More_Times_Fail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.AddGet_Of_TestNodeStateProperty_Succed() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Any_Should_Return_CorrectBoolean() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.AsEnumerable_Should_Return_CorrectItems() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Counts_ShouldBe_Correct() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Ctors_CorrectlyInit() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Ctors_With_WrongInit_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.EmptyProperties_Should_NotFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.OfType_Should_Return_CorrectObject() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Single_Should_Return_CorrectObject() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.SingleOrDefault_Should_Return_CorrectObject() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.ConsumeAsync_WithSessionFileArtifact() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.ConsumeAsync_WithTestNodeUpdatedMessage() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.PopulateTestNodeStatistics_WithDiscoveredAndPassedEventsForSameUid() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.PopulateTestNodeStatistics_WithDuplicateDiscoveredEvents() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.PopulateTestNodeStatistics_WithDuplicatePassedEvents() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.PopulateTestNodeStatistics_WithEventsForDifferentUids() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.PopulateTestNodeStatistics_WithEventsForSameUid() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.PopulateTestNodeStatistics_WithMissingNodeType() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerTelemetryTests.LogEvent_ForDiscovery() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerTelemetryTests.LogEvent_ForRun() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerTests.DiscoveryRequestCanBeCanceled() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerTests.ServerCanBeStartedAndAborted_TcpIp() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerTests.ServerCanInitialize() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.AddService_SameInstance_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.AddService_SameInstance_ShouldNotFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.AddService_TestFramework_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.AddService_TestFramework_ShouldNotFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.AddServices_SameInstance_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.AddServices_SameInstance_ShouldNotFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.Clone_WithFilter_Succeeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.Clone_WithoutFilter_Succeeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetService_InternalExtension_ShouldNotReturn() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServiceInternal_InternalExtension_ShouldReturn() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServiceInternal_InternalExtension_ShouldReturnOne() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServiceInternal_InternalExtension_SkipInternalOnlyExtensios_ShouldReturnNull() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServicesInternal_ExtensionMethod_InternalExtension_ShouldReturn() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServicesInternal_InternalExtension_FirstOnly_ShouldReturnOne() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServicesInternal_InternalExtension_ShouldNotReturn() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServicesInternal_InternalExtension_ShouldReturn() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.TryAddService_SameInstance_ShouldReturnFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.TryAddService_TestFramework_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.TryAddService_TestFramework_ShouldNotFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.SystemAsyncMonitorTests.AsyncMonitor_ShouldCorrectlyLock() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.CancellationAsync_Cancellation_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.CancellationAsync_CancellationWithArgument_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.CancellationAsync_NonCancelled_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.CancellationAsync_NonCancelledWithArgument_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.CancellationAsync_ObserveException_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.CancellationAsyncWithReturnValue_ObserveException_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.TimeoutAfterAsync_CancellationToken_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.TimeoutAfterAsync_CancellationTokenNone_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.TimeoutAfterAsync_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_SentinelIsWrittenOnlyWhenUserWouldSeeTheMessage() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_SentinelIsWrittenPerUserAndAvoidsShowingNoticeOnSubsequentRuns() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoBannerCommandLine_ShouldSuppressTelemetryMessage() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string, string) (variable: "DOTNET_NOLOGO", value: "0") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string, string) (variable: "DOTNET_NOLOGO", value: "1") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string, string) (variable: "DOTNET_NOLOGO", value: "true") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string, string) (variable: "TESTINGPLATFORM_NOBANNER", value: "0") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string, string) (variable: "TESTINGPLATFORM_NOBANNER", value: "1") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string, string) (variable: "TESTINGPLATFORM_NOBANNER", value: "true") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string, string) (variable: "DOTNET_CLI_TELEMETRY_OPTOUT", value: "0") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string, string) (variable: "DOTNET_CLI_TELEMETRY_OPTOUT", value: "1") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string, string) (variable: "DOTNET_CLI_TELEMETRY_OPTOUT", value: "true") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string, string) (variable: "TESTINGPLATFORM_TELEMETRY_OPTOUT", value: "0") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string, string) (variable: "TESTINGPLATFORM_TELEMETRY_OPTOUT", value: "1") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string, string) (variable: "TESTINGPLATFORM_TELEMETRY_OPTOUT", value: "true") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.ComposeFactory_InvalidComposition_ShouldFail(bool) (withParameter: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.ComposeFactory_InvalidComposition_ShouldFail(bool) (withParameter: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.DataConsumer_DuplicatedId_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.DataConsumer_DuplicatedIdWithCompositeFactory_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestApplicationLifecycleCallbacks_DuplicatedId_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHost_ComposeFactory_ShouldSucceed(bool) (withParameter: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHost_ComposeFactory_ShouldSucceed(bool) (withParameter: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHostController_ComposeFactory_ShouldSucceed(bool) (withParameter: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHostController_ComposeFactory_ShouldSucceed(bool) (withParameter: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHostControllerEnvironmentVariableProvider_DuplicatedId_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHostControllerEnvironmentVariableProvider_DuplicatedIdWithCompositeFactory_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHostControllerProcessLifetimeHandler_DuplicatedId_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHostControllerProcessLifetimeHandler_DuplicatedIdWithCompositeFactory_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestSessionLifetimeHandle_DuplicatedId_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestSessionLifetimeHandle_DuplicatedIdWithCompositeFactory_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_All_Skipped_Returns_Zero() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_Cancelled_Returns_TestSessionAborted() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_Failed_Tests_Returns_AtLeastOneTestFailed(Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_Failed_Tests_Returns_AtLeastOneTestFailed(Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_Failed_Tests_Returns_AtLeastOneTestFailed(Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_Failed_Tests_Returns_AtLeastOneTestFailed(Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_MinimumExpectedTests_Violated_Returns_MinimumExpectedTestsPolicyViolation() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_No_Tests_Ran_Returns_ZeroTestsRan() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_TestAdapter_Returns_TestAdapterTestSessionFailure() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "", expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: ";", expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "5", expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "5;", expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "5;7", expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "5;7;", expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "8", expectedExitCode: 0) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "8;", expectedExitCode: 0) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "8;2", expectedExitCode: 0) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "8;2;", expectedExitCode: 0) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: null, expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_OnDiscovery_No_Tests_Discovered_Returns_ZeroTests() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_OnDiscovery_Some_Tests_Discovered_Returns_Success() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestNodeUidTests.TestNodeUid_EqualityChecks_ShouldWorkAsExpected() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestNodeUidTests.TestNodeUid_NullValue_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.AndExpression() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.EscapeSequences_SupportsParentheses() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.EscapeSequences_SupportsWildcard() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.EscapeSequences_ThrowsIfLastCharIsAnEscapeChar() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.MatchAllFilter_DoNotAllowInMiddleOfFilter() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.MatchAllFilter_Invalid() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.MatchAllFilter_MatchesAnyPath() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.MatchAllFilter_MatchesSubpaths() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.MatchWildcard_MatchesSubstrings() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.OrExpression_WorksForLiteralStrings() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parameters_DisallowAtStart() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parameters_DisallowEmpty() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parameters_DisallowMultiple() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parameters_DisallowNested() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parameters_PropertyCheck() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parentheses_EnsuresOrdering() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parenthesis_DisallowSeparatorInside() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A%2FB[Other%2Fthing=KeyWithSlash]", nodePath: "/A%2FB", isMatched: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A%2FB[Other/thing=KeyWithSlash]", nodePath: "/A%2FB", isMatched: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A%2FB[ValueWithSlash=Some%2Fthing]", nodePath: "/A%2FB", isMatched: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A%2FB[ValueWithSlash=Some/thing]", nodePath: "/A%2FB", isMatched: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A/B[Other%2Fthing=KeyWithSlash]", nodePath: "/A/B", isMatched: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A/B[Other/thing=KeyWithSlash]", nodePath: "/A/B", isMatched: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A/B[ValueWithSlash=Some%2Fthing]", nodePath: "/A/B", isMatched: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A/B[ValueWithSlash=Some/thing]", nodePath: "/A/B", isMatched: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.TestNodeFilterNeedsUrlEncodingOfSlashes(string, string, bool) (filter: "/A%2FB", nodePath: "/A%2FB", isMatched: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.TestNodeFilterNeedsUrlEncodingOfSlashes(string, string, bool) (filter: "/A%2FB", nodePath: "/A/B", isMatched: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.TestNodeFilterNeedsUrlEncodingOfSlashes(string, string, bool) (filter: "/A/B", nodePath: "/A%2FB", isMatched: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.TestNodeFilterNeedsUrlEncodingOfSlashes(string, string, bool) (filter: "/A/B", nodePath: "/A/B", isMatched: true) diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/testsbaseline.txt b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/testsbaseline.txt deleted file mode 100644 index 0e8ff2271b..0000000000 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/testsbaseline.txt +++ /dev/null @@ -1,359 +0,0 @@ -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.CheckTestResultsDirectoryOverrideAndCreateItAsync_ResultsDirectoryIsNull_GetDefaultDirectory() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.CheckTestResultsDirectoryOverrideAndCreateItAsync_ResultsDirectoryIsNull_GetDirectoryFromCommandLineProvider() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.CheckTestResultsDirectoryOverrideAndCreateItAsync_ResultsDirectoryIsNull_GetDirectoryFromStore() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_CurrentWorkingDirectorySet_DirectoryIsNotNull() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetAndNoConfigurationProviders_DirectoryIsNull(string) (key: "testingPlatform:currentWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetAndNoConfigurationProviders_DirectoryIsNull(string) (key: "testingPlatform:resultDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetAndNoConfigurationProviders_DirectoryIsNull(string) (key: "testingPlatform:testHostWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNotNull(string) (key: "testingPlatform:currentWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNotNull(string) (key: "testingPlatform:resultDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNotNull(string) (key: "testingPlatform:testHostWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNull(string) (key: "testingPlatform:currentWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNull(string) (key: "testingPlatform:resultDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNull(string) (key: "testingPlatform:testHostWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_ResultDirectorySet_DirectoryIsNotNull() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AggregatedConfigurationTests.IndexerTest_TestHostWorkingDirectorySet_DirectoryIsNotNull() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ArgumentArityTests.ParseAndValidate_WhenOptionsGetsTheExpectedNumberOfArguments_ReturnsTrue() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ArgumentArityTests.ParseAndValidate_WhenOptionWithArityExactlyOneIsCalledWithoutArguments_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ArgumentArityTests.ParseAndValidate_WhenOptionWithArityExactlyOneIsCalledWithTwoArguments_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ArgumentArityTests.ParseAndValidate_WhenOptionWithArityOneOrMoreIsCalledWithoutArguments_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ArgumentArityTests.ParseAndValidate_WhenOptionWithArityZeroIsCalledWithOneArgument_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ArgumentArityTests.ParseAndValidate_WhenOptionWithArityZeroOrOneIsCalledWithTwoArguments_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AsynchronousMessageBusTests.Consumers_ConsumeData_ShouldNotMissAnyPayload() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AsynchronousMessageBusTests.DrainDataAsync_Loop_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AsynchronousMessageBusTests.MessageBus_WhenConsumerProducesAndConsumesTheSameType_ShouldNotConsumeWhatProducedByItself() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.AsynchronousMessageBusTests.UnexpectedTypePublished_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.GetOptionValue_OptionDoesNotExist_ReturnsNull() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.GetOptionValue_OptionExists_ReturnsOptionValue() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.IsHelpInvoked_HelpOptionSet_ReturnsTrue() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.IsInfoInvoked_InfoOptionSet_ReturnsTrue() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.IsVersionInvoked_VersionOptionSet_ReturnsTrue() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_DuplicateOption_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_EmptyCommandLineArguments_ReturnsTrue() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_InvalidArgumentArity_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_InvalidCommandLineArguments_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_InvalidOption_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_InvalidValidConfiguration_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_ReservedOptions_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_ReservedOptionsPrefix_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CommandLineHandlerTests.ParseAndValidateAsync_UnknownOption_ReturnsFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationExtensionsTests.ConfigurationExtensions_TestedMethod_ReturnsExpectedPath(string) (key: "testingPlatform:currentWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationExtensionsTests.ConfigurationExtensions_TestedMethod_ReturnsExpectedPath(string) (key: "testingPlatform:resultDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationExtensionsTests.ConfigurationExtensions_TestedMethod_ReturnsExpectedPath(string) (key: "testingPlatform:testHostWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationExtensionsTests.ConfigurationExtensions_TestedMethod_ThrowsArgumentNullException(string) (key: "testingPlatform:currentWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationExtensionsTests.ConfigurationExtensions_TestedMethod_ThrowsArgumentNullException(string) (key: "testingPlatform:resultDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationExtensionsTests.ConfigurationExtensions_TestedMethod_ThrowsArgumentNullException(string) (key: "testingPlatform:testHostWorkingDirectory") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.BuildAsync_ConfigurationSourceIsAsyncInitializableExtension_InitializeAsyncIsCalled() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.BuildAsync_ConfigurationSourcesNotEnabledAsync_ThrowsException() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.BuildAsync_EmptyConfigurationSources_ThrowsException() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [10] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [11] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [12] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [7] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [8] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJson(string, string, string?) [9] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [10] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [11] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [12] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [7] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [8] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.GetConfigurationValueFromJsonWithFileLoggerProvider(string, string, string?) [9] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConfigurationManagerTests.InvalidJson_Fail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDuration_InvalidInput_ShouldReturnNull(double?) (durationInMs: -1) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDuration_InvalidInput_ShouldReturnNull(double?) (durationInMs: null) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "0ms", time: "00 00 00 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "100ms", time: "00 00 00 100") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "10h 00m 00s 000ms", time: "10 00 00 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "10ms", time: "00 00 00 010") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "11m 00s 000ms", time: "00 11 00 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "11m 01s 100ms", time: "00 11 01 100") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "11s 100ms", time: "00 00 11 100") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "1d 01h 00m 00s 000ms", time: "25 00 00 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "1m 00s 000ms", time: "00 01 00 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "1ms", time: "00 00 00 001") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "1s 000ms", time: "00 00 01 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "1s 100ms", time: "00 00 01 100") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "2d 01h 00m 00s 000ms", time: "49 00 00 000") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ConsoleOutputDevice.ConsoleOutputDeviceTests.ToHumanReadableDurationTests(string, string) (expectedString: "59m 01s 100ms", time: "00 59 01 100") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CountDownEventTests.CountDownEvent_WaitAsync_Succeeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CountDownEventTests.CountDownEvent_WaitAsyncCancelled_Succeeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.CountDownEventTests.CountDownEvent_WaitAsyncCancelledByTimeout_Succeeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.FileLogger_NullFileSyncFlush_FileStreamCreated() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.FileLogger_NullFileSyncFlush_FileStreamCreationThrows() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool, bool) (syncFlush: false, fileExists: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool, bool) (syncFlush: false, fileExists: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool, bool) (syncFlush: true, fileExists: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool, bool) (syncFlush: true, fileExists: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FileLoggerTests.Write_IfMalformedUTF8_ShouldNotCrash() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.CanDeserializeTaskResponse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.DeserializeSpecificTypes(System.Type) (type: typeof(Microsoft.Testing.Platform.ServerMode.DiscoverRequestArgs)) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.DeserializeSpecificTypes(System.Type) (type: typeof(Microsoft.Testing.Platform.ServerMode.RunRequestArgs)) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (Artifact) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (AttachDebuggerInfoArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (CancelRequestArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (DiscoverResponseArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (ErrorMessage) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (InitializeResponseArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (KeyValuePair`2) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (LogEventArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (NotificationMessage) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (Object) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (ProcessInfoArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (RequestMessage) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (ResponseMessage) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (RunResponseArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (ServerCapabilities) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (ServerInfo) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (ServerTestingCapabilities) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (TelemetryEventArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (TestNode) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (TestNodeStateChangedEventArgs) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.FormatterUtilitiesTests.SerializeDeserialize_Succeed(System.Type) (TestNodeUpdateMessage) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.IPCTests.ConnectionNamedPipeServer_MultipleConnection_Succeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.IPCTests.SingleConnectionNamedPipeServer_MultipleConnection_Fails() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.IPCTests.SingleConnectionNamedPipeServer_RequestReplySerialization_Succeeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.JsoniteTests.Serialize_DateTimeOffset() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.JsonTests.DeserializePerson() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.JsonTests.DeserializePersonList() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.JsonTests.Serialize_Array() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.JsonTests.Serialize_ArrayOfObjects() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.JsonTests.Serialize_DateTimeOffset() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.JsonTests.Serialize_TestNodeAsync() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerFactoryTests.LoggerFactory_LoggerCreatedOnlyOnce() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_CheckEnabled(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_Log_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggerTests.Logger_LogAsync_FormattedStringIsCorrect(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogCritical_CallsLogWithLogLevelCritical() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogCriticalAsync_CallsLogAsyncWithLogLevelCritical() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogDebug_CallsLogWithLogLevelDebug() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogDebugAsync_CallsLogAsyncWithLogLevelDebug() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogError_CallsLogWithLogLevelError() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogError_CallsLogWithLogLevelErrorAndException() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogError_CallsLogWithLogLevelErrorAndMessageAndException() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogErrorAsync_CallsLogAsyncWithLogLevelError() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogErrorAsync_CallsLogAsyncWithLogLevelErrorAndException() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogErrorAsync_CallsLogAsyncWithLogLevelErrorAndMessageAndException() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogInformation_CallsLogWithLogLevelInformation() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogInformationAsync_CallsLogAsyncWithLogLevelInformation() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogTrace_CallsLogWithLogLevelTrace() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogTraceAsync_CallsLogAsyncWithLogLevelTrace() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogWarning_CallsLogWithLogLevelWarning() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.LoggingExtensionsTests.LoggerExtensions_LogWarningAsync_CallsLogAsyncWithLogLevelWarning() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_CheckDisabled(Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_Log_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.NopLoggerTests.NopLogger_LogAsync_NoFormatterCalls(Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Add_Of_TestNodeStateProperty_More_Than_One_Time_Fail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Add_Same_Instance_More_Times_Fail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.AddGet_Of_TestNodeStateProperty_Succed() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Any_Should_Return_CorrectBoolean() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.AsEnumerable_Should_Return_CorrectItems() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Counts_ShouldBe_Correct() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Ctors_CorrectlyInit() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Ctors_With_WrongInit_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.EmptyProperties_Should_NotFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.OfType_Should_Return_CorrectObject() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.Single_Should_Return_CorrectObject() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.PropertyBagTests.SingleOrDefault_Should_Return_CorrectObject() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.ConsumeAsync_WithSessionFileArtifact() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.ConsumeAsync_WithTestNodeUpdatedMessage() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.PopulateTestNodeStatistics_WithDiscoveredAndPassedEventsForSameUid() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.PopulateTestNodeStatistics_WithDuplicateDiscoveredEvents() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.PopulateTestNodeStatistics_WithDuplicatePassedEvents() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.PopulateTestNodeStatistics_WithEventsForDifferentUids() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.PopulateTestNodeStatistics_WithEventsForSameUid() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerDataConsumerServiceTests.PopulateTestNodeStatistics_WithMissingNodeType() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_Log(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [4] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [5] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerLoggerForwarderTests.ServerLoggerForwarder_LogAsync(Microsoft.Testing.Platform.Logging.LogLevel, Microsoft.Testing.Platform.Logging.LogLevel) [6] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerTelemetryTests.LogEvent_ForDiscovery() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerTelemetryTests.LogEvent_ForRun() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerTests.DiscoveryRequestCanBeCanceled() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerTests.ServerCanBeStartedAndAborted_TcpIp() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServerTests.ServerCanInitialize() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.AddService_SameInstance_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.AddService_SameInstance_ShouldNotFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.AddService_TestFramework_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.AddService_TestFramework_ShouldNotFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.AddServices_SameInstance_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.AddServices_SameInstance_ShouldNotFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.Clone_WithFilter_Succeeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.Clone_WithoutFilter_Succeeded() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetService_InternalExtension_ShouldNotReturn() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServiceInternal_InternalExtension_ShouldReturn() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServiceInternal_InternalExtension_ShouldReturnOne() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServiceInternal_InternalExtension_SkipInternalOnlyExtensios_ShouldReturnNull() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServicesInternal_ExtensionMethod_InternalExtension_ShouldReturn() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServicesInternal_InternalExtension_FirstOnly_ShouldReturnOne() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServicesInternal_InternalExtension_ShouldNotReturn() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.GetServicesInternal_InternalExtension_ShouldReturn() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.TryAddService_SameInstance_ShouldReturnFalse() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.TryAddService_TestFramework_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.ServiceProviderTests.TryAddService_TestFramework_ShouldNotFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.SystemAsyncMonitorTests.AsyncMonitor_ShouldCorrectlyLock() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.CancellationAsync_Cancellation_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.CancellationAsync_CancellationWithArgument_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.CancellationAsync_NonCancelled_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.CancellationAsync_NonCancelledWithArgument_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.CancellationAsync_ObserveException_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.CancellationAsyncWithReturnValue_ObserveException_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.TimeoutAfterAsync_CancellationToken_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.TimeoutAfterAsync_CancellationTokenNone_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TaskExtensionsTests.TimeoutAfterAsync_Succeeds() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_SentinelIsWrittenOnlyWhenUserWouldSeeTheMessage() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_SentinelIsWrittenPerUserAndAvoidsShowingNoticeOnSubsequentRuns() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoBannerCommandLine_ShouldSuppressTelemetryMessage() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string, string) (variable: "DOTNET_NOLOGO", value: "0") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string, string) (variable: "DOTNET_NOLOGO", value: "1") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string, string) (variable: "DOTNET_NOLOGO", value: "true") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string, string) (variable: "TESTINGPLATFORM_NOBANNER", value: "0") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string, string) (variable: "TESTINGPLATFORM_NOBANNER", value: "1") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string, string) (variable: "TESTINGPLATFORM_NOBANNER", value: "true") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string, string) (variable: "DOTNET_CLI_TELEMETRY_OPTOUT", value: "0") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string, string) (variable: "DOTNET_CLI_TELEMETRY_OPTOUT", value: "1") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string, string) (variable: "DOTNET_CLI_TELEMETRY_OPTOUT", value: "true") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string, string) (variable: "TESTINGPLATFORM_TELEMETRY_OPTOUT", value: "0") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string, string) (variable: "TESTINGPLATFORM_TELEMETRY_OPTOUT", value: "1") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TelemetryManagerTests.TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string, string) (variable: "TESTINGPLATFORM_TELEMETRY_OPTOUT", value: "true") -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.ComposeFactory_InvalidComposition_ShouldFail(bool) (withParameter: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.ComposeFactory_InvalidComposition_ShouldFail(bool) (withParameter: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.DataConsumer_DuplicatedId_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.DataConsumer_DuplicatedIdWithCompositeFactory_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestApplicationLifecycleCallbacks_DuplicatedId_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHost_ComposeFactory_ShouldSucceed(bool) (withParameter: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHost_ComposeFactory_ShouldSucceed(bool) (withParameter: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHostController_ComposeFactory_ShouldSucceed(bool) (withParameter: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHostController_ComposeFactory_ShouldSucceed(bool) (withParameter: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHostControllerEnvironmentVariableProvider_DuplicatedId_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHostControllerEnvironmentVariableProvider_DuplicatedIdWithCompositeFactory_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHostControllerProcessLifetimeHandler_DuplicatedId_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestHostControllerProcessLifetimeHandler_DuplicatedIdWithCompositeFactory_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestSessionLifetimeHandle_DuplicatedId_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationBuilderTests.TestSessionLifetimeHandle_DuplicatedIdWithCompositeFactory_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_All_Skipped_Returns_Zero() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_Cancelled_Returns_TestSessionAborted() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_Failed_Tests_Returns_AtLeastOneTestFailed(Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty) [0] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_Failed_Tests_Returns_AtLeastOneTestFailed(Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty) [1] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_Failed_Tests_Returns_AtLeastOneTestFailed(Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty) [2] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_Failed_Tests_Returns_AtLeastOneTestFailed(Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty) [3] -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_MinimumExpectedTests_Violated_Returns_MinimumExpectedTestsPolicyViolation() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_No_Tests_Ran_Returns_ZeroTestsRan() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_If_TestAdapter_Returns_TestAdapterTestSessionFailure() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: ";", expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "", expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "5;", expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "5;7;", expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "5;7", expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "5", expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "8;", expectedExitCode: 0) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "8;2;", expectedExitCode: 0) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "8;2", expectedExitCode: 0) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: "8", expectedExitCode: 0) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_IgnoreExitCodes(string, int) (argument: null, expectedExitCode: 8) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_OnDiscovery_No_Tests_Discovered_Returns_ZeroTests() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestApplicationResultTests.GetProcessExitCodeAsync_OnDiscovery_Some_Tests_Discovered_Returns_Success() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestNodeUidTests.TestNodeUid_EqualityChecks_ShouldWorkAsExpected() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TestNodeUidTests.TestNodeUid_NullValue_ShouldFail() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.AndExpression() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.EscapeSequences_SupportsParentheses() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.EscapeSequences_SupportsWildcard() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.EscapeSequences_ThrowsIfLastCharIsAnEscapeChar() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.MatchAllFilter_DoNotAllowInMiddleOfFilter() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.MatchAllFilter_Invalid() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.MatchAllFilter_MatchesAnyPath() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.MatchAllFilter_MatchesSubpaths() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.MatchWildcard_MatchesSubstrings() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.OrExpression_WorksForLiteralStrings() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parameters_DisallowAtStart() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parameters_DisallowEmpty() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parameters_DisallowMultiple() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parameters_DisallowNested() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parameters_PropertyCheck() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parentheses_EnsuresOrdering() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.Parenthesis_DisallowSeparatorInside() -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A/B[Other/thing=KeyWithSlash]", nodePath: "/A/B", isMatched: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A/B[Other%2Fthing=KeyWithSlash]", nodePath: "/A/B", isMatched: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A/B[ValueWithSlash=Some/thing]", nodePath: "/A/B", isMatched: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A/B[ValueWithSlash=Some%2Fthing]", nodePath: "/A/B", isMatched: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A%2FB[Other/thing=KeyWithSlash]", nodePath: "/A%2FB", isMatched: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A%2FB[Other%2Fthing=KeyWithSlash]", nodePath: "/A%2FB", isMatched: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A%2FB[ValueWithSlash=Some/thing]", nodePath: "/A%2FB", isMatched: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.PropertiesDoNotNeedUrlEncodingOfSlashes(string, string, bool) (filter: "/A%2FB[ValueWithSlash=Some%2Fthing]", nodePath: "/A%2FB", isMatched: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.TestNodeFilterNeedsUrlEncodingOfSlashes(string, string, bool) (filter: "/A/B", nodePath: "/A/B", isMatched: true) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.TestNodeFilterNeedsUrlEncodingOfSlashes(string, string, bool) (filter: "/A/B", nodePath: "/A%2FB", isMatched: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.TestNodeFilterNeedsUrlEncodingOfSlashes(string, string, bool) (filter: "/A%2FB", nodePath: "/A/B", isMatched: false) -Microsoft.Testing.Platform.UnitTests.Microsoft.Testing.Platform.UnitTests.TreeNodeFilterTests.TestNodeFilterNeedsUrlEncodingOfSlashes(string, string, bool) (filter: "/A%2FB", nodePath: "/A%2FB", isMatched: true) diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs index 87611fb670..d188c6d3cd 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #nullable enable @@ -24,8 +24,7 @@ public void AreNotEqualShouldFailWhenNotEqualType() public void AreNotEqualShouldFailWhenNotEqualTypeWithMessage() { Exception ex = VerifyThrows(() => Assert.AreNotEqual(1, 1, "A Message")); - Verify(ex != null); - Verify(ex!.Message.Contains("A Message")); + Verify(ex.Message.Contains("A Message")); } public void AreNotEqualShouldFailWhenNotEqualString() @@ -38,8 +37,7 @@ public void AreNotEqualShouldFailWhenNotEqualString() public void AreNotEqualShouldFailWhenNotEqualStringWithMessage() { Exception ex = VerifyThrows(() => Assert.AreNotEqual("A", "A", "A Message")); - Verify(ex != null); - Verify(ex!.Message.Contains("A Message")); + Verify(ex.Message.Contains("A Message")); } [SuppressMessage("Globalization", "CA1304:Specify CultureInfo", Justification = "Testing the API without the culture")] @@ -60,8 +58,7 @@ public void AreNotEqualShouldFailWhenNotEqualInt() public void AreNotEqualShouldFailWhenNotEqualIntWithMessage() { Exception? ex = VerifyThrows(() => Assert.AreNotEqual(1, 1, "A Message")); - Verify(ex is not null); - Verify(ex!.Message.Contains("A Message")); + Verify(ex.Message.Contains("A Message")); } public void AreNotEqualShouldFailWhenNotEqualLong() @@ -73,9 +70,8 @@ public void AreNotEqualShouldFailWhenNotEqualLong() public void AreNotEqualShouldFailWhenNotEqualLongWithMessage() { - Exception? ex = VerifyThrows(() => Assert.AreNotEqual(1L, 1L, "A Message")); - Verify(ex is not null); - Verify(ex!.Message.Contains("A Message")); + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1L, 1L, "A Message")); + Verify(ex.Message.Contains("A Message")); } public void AreNotEqualShouldFailWhenNotEqualLongWithDelta() @@ -94,9 +90,8 @@ public void AreNotEqualShouldFailWhenNotEqualDecimal() public void AreNotEqualShouldFailWhenNotEqualDecimalWithMessage() { - Exception? ex = VerifyThrows(() => Assert.AreNotEqual(0.1M, 0.1M, "A Message")); - Verify(ex is not null); - Verify(ex!.Message.Contains("A Message")); + Exception ex = VerifyThrows(() => Assert.AreNotEqual(0.1M, 0.1M, "A Message")); + Verify(ex.Message.Contains("A Message")); } public void AreNotEqualShouldFailWhenNotEqualDecimalWithDelta() @@ -116,8 +111,7 @@ public void AreNotEqualShouldFailWhenNotEqualDouble() public void AreNotEqualShouldFailWhenNotEqualDoubleWithMessage() { Exception? ex = VerifyThrows(() => Assert.AreNotEqual(0.1, 0.1, "A Message")); - Verify(ex is not null); - Verify(ex!.Message.Contains("A Message")); + Verify(ex.Message.Contains("A Message")); } public void AreNotEqualShouldFailWhenNotEqualDoubleWithDelta() @@ -136,9 +130,8 @@ public void AreNotEqualShouldFailWhenFloatDouble() public void AreNotEqualShouldFailWhenFloatDoubleWithMessage() { - Exception? ex = VerifyThrows(() => Assert.AreNotEqual(100E-2, 100E-2, "A Message")); - Verify(ex is not null); - Verify(ex!.Message.Contains("A Message")); + Exception ex = VerifyThrows(() => Assert.AreNotEqual(100E-2, 100E-2, "A Message")); + Verify(ex.Message.Contains("A Message")); } public void AreNotEqualShouldFailWhenNotEqualFloatWithDelta() @@ -157,9 +150,8 @@ public void AreEqualShouldFailWhenNotEqualType() public void AreEqualShouldFailWhenNotEqualTypeWithMessage() { - Exception? ex = VerifyThrows(() => Assert.AreEqual(null, "string", "A Message")); - Verify(ex is not null); - Verify(ex!.Message.Contains("A Message")); + Exception ex = VerifyThrows(() => Assert.AreEqual(null, "string", "A Message")); + Verify(ex.Message.Contains("A Message")); } public void AreEqual_WithTurkishCultureAndIgnoreCase_Throws() @@ -169,8 +161,7 @@ public void AreEqual_WithTurkishCultureAndIgnoreCase_Throws() var turkishCulture = new CultureInfo("tr-TR"); // In the tr-TR culture, "i" and "I" are not considered equal when doing a case-insensitive comparison. - Exception? ex = VerifyThrows(() => Assert.AreEqual(expected, actual, true, turkishCulture)); - Verify(ex is not null); + VerifyThrows(() => Assert.AreEqual(expected, actual, true, turkishCulture)); } public void AreEqual_WithEnglishCultureAndIgnoreCase_DoesNotThrow() @@ -190,8 +181,7 @@ public void AreEqual_WithEnglishCultureAndDoesNotIgnoreCase_Throws() var englishCulture = new CultureInfo("en-EN"); // Won't ignore case. - Exception? ex = VerifyThrows(() => Assert.AreEqual(expected, actual, false, englishCulture)); - Verify(ex is not null); + VerifyThrows(() => Assert.AreEqual(expected, actual, false, englishCulture)); } public void AreEqual_WithTurkishCultureAndDoesNotIgnoreCase_Throws() @@ -306,9 +296,8 @@ public void AreEqualShouldFailWhenFloatDouble() public void AreEqualShouldFailWhenFloatDoubleWithMessage() { - Exception? ex = VerifyThrows(() => Assert.AreEqual(100E-2, 200E-2, "A Message")); - Verify(ex is not null); - Verify(ex!.Message.Contains("A Message")); + Exception ex = VerifyThrows(() => Assert.AreEqual(100E-2, 200E-2, "A Message")); + Verify(ex.Message.Contains("A Message")); } public void AreEqualShouldFailWhenNotEqualFloatWithDelta() @@ -330,7 +319,7 @@ public void AreEqualTwoObjectsDifferentTypeShouldFail() static void Action() => Assert.AreEqual(new object(), 1); Exception ex = VerifyThrows(Action); Verify(ex is AssertFailedException); - Verify(ex!.Message.Contains("Assert.AreEqual failed. Expected:. Actual:<1 (System.Int32)>.")); + Verify(ex.Message.Contains("Assert.AreEqual failed. Expected:. Actual:<1 (System.Int32)>.")); } public void AreEqualWithTypeOverridingEqualsShouldWork() @@ -393,6 +382,14 @@ public void AreEqualUsingCustomIEquatable() VerifyThrows(() => Assert.AreEqual(instanceOfB, instanceOfA)); } + public void AreEqualUsingDynamicsDoesNotFail() + { + Assert.AreEqual((dynamic?)null, (dynamic?)null); + Assert.AreEqual((dynamic)1, (dynamic)1); + Assert.AreEqual((dynamic)"a", (dynamic)"a"); + Assert.AreEqual((dynamic)'a', (dynamic)'a'); + } + private CultureInfo? GetCultureInfo() => CultureInfo.CurrentCulture; private class TypeOverridesEquals diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.InconclusiveTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.InconclusiveTests.cs index 18be407da6..a92031738a 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.InconclusiveTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.InconclusiveTests.cs @@ -1,6 +1,8 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics.CodeAnalysis; + using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; @@ -18,6 +20,7 @@ public void InconclusiveDoesNotThrowWhenMessageContainsInvalidStringFormatCompos } // See https://github.com/dotnet/sdk/issues/25373 + [SuppressMessage("Usage", "CA2241:Provide correct arguments to formatting methods", Justification = "We want to test invalid format")] public void InconclusiveThrowsWhenMessageContainsInvalidStringFormatComposite() { Exception ex = VerifyThrows(() => Assert.Inconclusive("{", "arg")); diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.cs index 03a7e7e7b8..49a2147d2d 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.cs @@ -84,7 +84,7 @@ public void ThrowsExceptionAsyncShouldThrowAssertionOnNoException() { Task t = Assert.ThrowsExceptionAsync( async () => await Task.Delay(5).ConfigureAwait(false)); - Exception ex = VerifyThrows(() => t.Wait()); + Exception ex = VerifyThrows(t.Wait); Verify(ex is not null); @@ -103,7 +103,7 @@ public void ThrowsExceptionAsyncShouldThrowAssertionOnWrongException() await Task.Delay(5).ConfigureAwait(false); throw new FormatException(); }); - Exception ex = VerifyThrows(() => t.Wait()); + Exception ex = VerifyThrows(t.Wait); Verify(ex is not null); @@ -119,7 +119,7 @@ public void ThrowsExceptionAsyncWithMessageShouldThrowAssertionOnNoException() Task t = Assert.ThrowsExceptionAsync( async () => await Task.Delay(5).ConfigureAwait(false), "The world is not on fire."); - Exception ex = VerifyThrows(() => t.Wait()); + Exception ex = VerifyThrows(t.Wait); Verify(ex is not null); @@ -139,7 +139,7 @@ public void ThrowsExceptionAsyncWithMessageShouldThrowAssertionOnWrongException( throw new FormatException(); }, "Happily ever after."); - Exception ex = VerifyThrows(() => t.Wait()); + Exception ex = VerifyThrows(t.Wait); Verify(ex is not null); @@ -194,7 +194,7 @@ public void ThrowsExceptionAsyncWithMessageAndParamsShouldThrowAssertionOnNoExce "ta", "da", 123); - Exception ex = VerifyThrows(() => t.Wait()); + Exception ex = VerifyThrows(t.Wait); Verify(ex is not null); @@ -216,7 +216,7 @@ public void ThrowsExceptionAsyncWithMessageAndParamsShouldThrowAssertionOnWrongE "Happily ever after. {0} {1}.", "The", "End"); - Exception ex = VerifyThrows(() => t.Wait()); + Exception ex = VerifyThrows(t.Wait); Verify(ex is not null); diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs index 30dc63b532..907ae8ac30 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #nullable enable @@ -242,6 +242,113 @@ public void CollectionAssertAreNotEqualComparerMessageParametersNullabilityPostC comparer.ToString(); // no warning } + public void CollectionAssertAreEquivalent_SameItemsWithDifferentOrder_DoesNotThrow() + { + ICollection? collection1 = GetMatchingSuperSet(); + ICollection? collection2 = GetReversedMatchingSuperSet(); + CollectionAssert.AreEquivalent(collection1, collection2); + } + + public void CollectionAssertAreEquivalent_WithMatchingNullableSets_DoesNotThrow() + { + ICollection? retSetWithNulls = new[] { "item", null }; + + ICollection? getMatchingSetWithNulls = new[] { "item", null }; + + CollectionAssert.AreEquivalent(retSetWithNulls, getMatchingSetWithNulls); + } + + public void CollectionAssertAreEquivalent_FailWhenNotEquivalent_WithMessage() + { + ICollection? collection1 = GetCollection(); + ICollection? collection2 = GetMatchingSuperSet(); + Exception ex = VerifyThrows(() => CollectionAssert.AreEquivalent(collection1, collection2, "message")); + Verify(ex.Message.Contains("message")); + } + + public void CollectionAssertAreEquivalent_FailWhenNotEquivalent_WithMessageAndParams() + { + ICollection? collection1 = GetCollection(); + ICollection? collection2 = GetMatchingSuperSet(); + Exception ex = VerifyThrows(() => CollectionAssert.AreEquivalent(collection1, collection2, "message format {0} {1}", 1, 2)); + Verify(ex.Message.Contains("message")); + } + + public void CollectionAssertAreEquivalent_WithInsensitiveCaseComparer_DoesNotThrow() + { + ICollection? collection1 = GetMatchingSuperSet(); + ICollection? collection2 = GetLettersCaseMismatchingSuperSet(); + CollectionAssert.AreEquivalent(collection1?.Cast(), collection2?.Cast(), new CaseInsensitiveEqualityComparer()); + } + + public void CollectionAssertAreEquivalent_FailsWithInsensitiveCaseComparer_WithMessage() + { + ICollection? collection1 = GetCollection(); + ICollection? collection2 = GetLettersCaseMismatchingSuperSet(); + Exception ex = VerifyThrows(() => CollectionAssert.AreEquivalent(collection1?.Cast(), collection2?.Cast(), new CaseInsensitiveEqualityComparer(), "message")); + Verify(ex.Message.Contains("message")); + } + + public void CollectionAssertAreEquivalent_FailsWithInsensitiveCaseComparer_WithMessageAndParams() + { + ICollection? collection1 = GetCollection(); + ICollection? collection2 = GetLettersCaseMismatchingSuperSet(); + Exception ex = VerifyThrows(() => CollectionAssert.AreEquivalent(collection1?.Cast(), collection2?.Cast(), new CaseInsensitiveEqualityComparer(), "message format {0} {1}", 1, 2)); + Verify(ex.Message.Contains("message")); + } + + public void CollectionAssertAreNotEquivalent_SameItemsWithDifferentOrder_DoesNotThrow() + { + ICollection? collection1 = GetCollection(); + ICollection? collection2 = GetMatchingSuperSet(); + CollectionAssert.AreNotEquivalent(collection1, collection2); + } + + public void CollectionAssertAreNotEquivalent_FailWhenNotEquivalent_WithMessage() + { + ICollection? collection1 = GetReversedMatchingSuperSet(); + ICollection? collection2 = GetMatchingSuperSet(); + Exception ex = VerifyThrows(() => CollectionAssert.AreNotEquivalent(collection1, collection2, "message")); + Verify(ex.Message.Contains("message")); + } + + public void CollectionAssertAreNotEquivalent_FailWhenNotEquivalent_WithMessageAndParams() + { + ICollection? collection1 = GetReversedMatchingSuperSet(); + ICollection? collection2 = GetMatchingSuperSet(); + Exception ex = VerifyThrows(() => CollectionAssert.AreNotEquivalent(collection1, collection2, "message format {0} {1}", 1, 2)); + Verify(ex.Message.Contains("message")); + } + + public void CollectionAssertAreNotEquivalent_WithInsensitiveCaseComparer_DoesNotThrow() + { + ICollection? collection1 = GetCollection(); + ICollection? collection2 = GetMatchingSuperSet(); + CollectionAssert.AreNotEquivalent(collection1?.Cast(), collection2?.Cast(), EqualityComparer.Default); + } + + public void CollectionAssertAreNotEquivalent_FailsWithInsensitiveCaseComparer_WithMessage() + { + ICollection? collection1 = GetMatchingSuperSet(); + ICollection? collection2 = GetLettersCaseMismatchingSuperSet(); + Exception ex = VerifyThrows(() => CollectionAssert.AreNotEquivalent(collection1?.Cast(), collection2?.Cast(), new CaseInsensitiveNotEqualityComparer(), "message")); + Verify(ex.Message.Contains("message")); + } + + public void CollectionAssertAreNotEquivalent_FailsWithInsensitiveCaseComparer_WithMessageAndParams() + { + ICollection? collection1 = GetMatchingSuperSet(); + ICollection? collection2 = GetLettersCaseMismatchingSuperSet(); + Exception ex = VerifyThrows(() => CollectionAssert.AreNotEquivalent(collection1?.Cast(), collection2?.Cast(), new CaseInsensitiveNotEqualityComparer(), "message format {0} {1}", 1, 2)); + Verify(ex.Message.Contains("message")); + } + + public void CollectionAssertAreNotEquivalent_FailsWithTwoNullsAndComparer_WithMessageAndParams() + { + Exception ex = VerifyThrows(() => CollectionAssert.AreNotEquivalent(null, null, new CaseInsensitiveNotEqualityComparer(), "message format {0} {1}", 1, 2)); + Verify(ex.Message.Contains("message")); + } + #pragma warning disable CA1859 // Use concrete types when possible for improved performance private ICollection? GetCollection() => new[] { "item" }; @@ -251,6 +358,10 @@ public void CollectionAssertAreNotEqualComparerMessageParametersNullabilityPostC private ICollection? GetMatchingSuperSet() => new[] { "item", "item2" }; + private ICollection? GetLettersCaseMismatchingSuperSet() => new[] { "Item", "iTem2" }; + + private ICollection? GetReversedMatchingSuperSet() => new[] { "item2", "item" }; + private ICollection? GetNotMatchingSuperSet() => new[] { "item3" }; private Type? GetStringType() => typeof(string); @@ -262,4 +373,18 @@ private class ObjectComparer : IComparer { int IComparer.Compare(object? x, object? y) => Equals(x, y) ? 0 : -1; } + + private class CaseInsensitiveEqualityComparer : IEqualityComparer + { + public bool Equals(string? x, string? y) => string.Equals(x, y, StringComparison.OrdinalIgnoreCase); + + public int GetHashCode(string obj) => obj.ToUpperInvariant().GetHashCode(); + } + + private class CaseInsensitiveNotEqualityComparer : IEqualityComparer + { + public bool Equals(string? x, string? y) => !string.Equals(x, y, StringComparison.OrdinalIgnoreCase); + + public int GetHashCode(string obj) => obj.ToUpperInvariant().GetHashCode(); + } } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/StringAssertTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/StringAssertTests.cs index b735fa2184..21ef26548d 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/StringAssertTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/StringAssertTests.cs @@ -1,8 +1,9 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #nullable enable +using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -22,7 +23,7 @@ public void StringAssertContains() string actual = "The quick brown fox jumps over the lazy dog."; string notInString = "I'm not in the string above"; Exception ex = VerifyThrows(() => StringAssert.Contains(actual, notInString)); - Verify(ex!.Message.Contains("StringAssert.Contains failed")); + Verify(ex.Message.Contains("StringAssert.Contains failed")); } public void StringAssertStartsWith() @@ -30,7 +31,7 @@ public void StringAssertStartsWith() string actual = "The quick brown fox jumps over the lazy dog."; string notInString = "I'm not in the string above"; Exception ex = VerifyThrows(() => StringAssert.StartsWith(actual, notInString)); - Verify(ex!.Message.Contains("StringAssert.StartsWith failed")); + Verify(ex.Message.Contains("StringAssert.StartsWith failed")); } public void StringAssertEndsWith() @@ -38,7 +39,7 @@ public void StringAssertEndsWith() string actual = "The quick brown fox jumps over the lazy dog."; string notInString = "I'm not in the string above"; Exception ex = VerifyThrows(() => StringAssert.EndsWith(actual, notInString)); - Verify(ex!.Message.Contains("StringAssert.EndsWith failed")); + Verify(ex.Message.Contains("StringAssert.EndsWith failed")); } public void StringAssertDoesNotMatch() @@ -46,7 +47,7 @@ public void StringAssertDoesNotMatch() string actual = "The quick brown fox jumps over the lazy dog."; Regex doesMatch = new("quick brown fox"); Exception ex = VerifyThrows(() => StringAssert.DoesNotMatch(actual, doesMatch)); - Verify(ex!.Message.Contains("StringAssert.DoesNotMatch failed")); + Verify(ex.Message.Contains("StringAssert.DoesNotMatch failed")); } public void StringAssertContainsIgnoreCase_DoesNotThrow() @@ -74,17 +75,18 @@ public void StringAssertEndsWithIgnoreCase_DoesNotThrow() public void StringAssertContainsDoesNotThrowFormatException() { Exception ex = VerifyThrows(() => StringAssert.Contains(":-{", "x")); - Verify(ex!.Message.Contains("StringAssert.Contains failed")); + Verify(ex.Message.Contains("StringAssert.Contains failed")); } // See https://github.com/dotnet/sdk/issues/25373 public void StringAssertContainsDoesNotThrowFormatExceptionWithArguments() { Exception ex = VerifyThrows(() => StringAssert.Contains("{", "x", "message {0}", "arg")); - Verify(ex!.Message.Contains("StringAssert.Contains failed")); + Verify(ex.Message.Contains("StringAssert.Contains failed")); } // See https://github.com/dotnet/sdk/issues/25373 + [SuppressMessage("Usage", "CA2241:Provide correct arguments to formatting methods", Justification = "We want to test invalid format")] public void StringAssertContainsFailsIfMessageIsInvalidStringFormatComposite() { Exception ex = VerifyThrows(() => StringAssert.Contains("a", "b", "message {{0}", "arg")); diff --git a/test/UnitTests/TestFramework.UnitTests/Attributes/DataRowAttributeTests.cs b/test/UnitTests/TestFramework.UnitTests/Attributes/DataRowAttributeTests.cs index 90ebf4d91a..11348adc73 100644 --- a/test/UnitTests/TestFramework.UnitTests/Attributes/DataRowAttributeTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Attributes/DataRowAttributeTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Reflection; @@ -13,6 +13,11 @@ namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests.Attributes public class DataRowAttributeTests : TestContainer { + public DataRowAttributeTests() + { + DataRowAttribute.TestIdGenerationStrategy = TestIdGenerationStrategy.FullyQualified; + } + public void DefaultConstructorSetsEmptyArrayPassed() { var dataRow = new DataRowAttribute(); @@ -61,8 +66,8 @@ public void ConstructorShouldSetMultipleDataArrays() var dataRow = new DataRowAttribute(new[] { "a" }, new[] { "b" }); Verify(dataRow.Data.Length == 2); - Verify(dataRow.Data[0] is string[] array1 && array1.SequenceEqual(new[] { "a" })); - Verify(dataRow.Data[1] is string[] array2 && array2.SequenceEqual(new[] { "b" })); + Verify(dataRow.Data[0] is string[] array1 && array1.SequenceEqual(["a"])); + Verify(dataRow.Data[1] is string[] array2 && array2.SequenceEqual(["b"])); } public void GetDataShouldReturnDataPassed() @@ -79,18 +84,18 @@ public void GetDisplayNameShouldReturnAppropriateName() var dummyTestClass = new DummyTestClass(); MethodInfo testMethodInfo = dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("DataRowTestMethod"); - string[] data = new string[] { "First", "Second", null }; - string[] data1 = new string[] { null, "First", "Second" }; - string[] data2 = new string[] { "First", null, "Second" }; + string[] data = ["First", "Second", null]; + string[] data1 = [null, "First", "Second"]; + string[] data2 = ["First", null, "Second"]; string displayName = dataRowAttribute.GetDisplayName(testMethodInfo, data); - Verify(displayName == "DataRowTestMethod (First,Second,)"); + Verify(displayName == "DataRowTestMethod (\"First\",\"Second\",null)"); displayName = dataRowAttribute.GetDisplayName(testMethodInfo, data1); - Verify(displayName == "DataRowTestMethod (,First,Second)"); + Verify(displayName == "DataRowTestMethod (null,\"First\",\"Second\")"); displayName = dataRowAttribute.GetDisplayName(testMethodInfo, data2); - Verify(displayName == "DataRowTestMethod (First,,Second)"); + Verify(displayName == "DataRowTestMethod (\"First\",null,\"Second\")"); } public void GetDisplayNameShouldReturnSpecifiedDisplayName() @@ -103,7 +108,7 @@ public void GetDisplayNameShouldReturnSpecifiedDisplayName() var dummyTestClass = new DummyTestClass(); MethodInfo testMethodInfo = dummyTestClass.GetType().GetTypeInfo().GetDeclaredMethod("DataRowTestMethod"); - string[] data = new string[] { "First", "Second", null }; + string[] data = ["First", "Second", null]; string displayName = dataRowAttribute.GetDisplayName(testMethodInfo, data); Verify(displayName == "DataRowTestWithDisplayName"); @@ -112,7 +117,7 @@ public void GetDisplayNameShouldReturnSpecifiedDisplayName() public void GetDisplayNameForArrayOfOneItem() { // Arrange - var dataRow = new DataRowAttribute(new[] { "a" }); + var dataRow = new DataRowAttribute(["a"]); var methodInfoMock = new Mock(); methodInfoMock.SetupGet(x => x.Name).Returns("MyMethod"); @@ -120,7 +125,7 @@ public void GetDisplayNameForArrayOfOneItem() string displayName = dataRow.GetDisplayName(methodInfoMock.Object, dataRow.Data); // Assert - Verify(displayName == "MyMethod (System.String[])"); + Verify(displayName == "MyMethod ([\"a\"])"); } public void GetDisplayName_AfterOverriding_GetsTheNewDisplayName() @@ -139,7 +144,7 @@ public void GetDisplayName_AfterOverriding_GetsTheNewDisplayName() public void GetDisplayNameForArrayOfMultipleItems() { // Arrange - var dataRow = new DataRowAttribute(new[] { "a", "b", "c" }); + var dataRow = new DataRowAttribute(["a", "b", "c"]); var methodInfoMock = new Mock(); methodInfoMock.SetupGet(x => x.Name).Returns("MyMethod"); @@ -147,7 +152,7 @@ public void GetDisplayNameForArrayOfMultipleItems() string displayName = dataRow.GetDisplayName(methodInfoMock.Object, dataRow.Data); // Assert - Verify(displayName == "MyMethod (System.String[])"); + Verify(displayName == "MyMethod ([\"a\",\"b\",\"c\"])"); } public void GetDisplayNameForMultipleArraysOfOneItem() @@ -161,7 +166,7 @@ public void GetDisplayNameForMultipleArraysOfOneItem() string displayName = dataRow.GetDisplayName(methodInfoMock.Object, dataRow.Data); // Assert - Verify(displayName == "MyMethod (System.String[],System.String[])"); + Verify(displayName == "MyMethod ([\"a\"],[\"1\"])"); } public void GetDisplayNameForMultipleArraysOfMultipleItems() @@ -175,16 +180,39 @@ public void GetDisplayNameForMultipleArraysOfMultipleItems() string displayName = dataRow.GetDisplayName(methodInfoMock.Object, dataRow.Data); // Assert - Verify(displayName == "MyMethod (System.String[],System.String[])"); + Verify(displayName == "MyMethod ([\"a\",\"b\",\"c\"],[\"1\",\"2\",\"3\"])"); } - private class DummyDataRowAttribute : DataRowAttribute + public void GetDisplayNameForMultipleArraysOfMultipleItemsValueTypes() { - public DummyDataRowAttribute() - : base() - { - } + // Arrange + var dataRow = new DataRowAttribute(new[] { 1, 2, 3 }, new[] { 4, 5, 6 }); + var methodInfoMock = new Mock(); + methodInfoMock.SetupGet(x => x.Name).Returns("MyMethod"); + + // Act + string displayName = dataRow.GetDisplayName(methodInfoMock.Object, dataRow.Data); + + // Assert + Verify(displayName == "MyMethod ([1,2,3],[4,5,6])"); + } + public void GetDisplayNameForMultipleArraysOfArraysOfMultipleItems() + { + // Arrange + var dataRow = new DataRowAttribute(new[] { ["a", "b", "c"], ["d", "e", "f"], new[] { "gh", "ij", "kl" } }, new[] { 'm', 'n', 'o' }, new[] { ["1", "2", "3"], ["4", "5", "6"], new[] { "7", "8", "9" } }); + var methodInfoMock = new Mock(); + methodInfoMock.SetupGet(x => x.Name).Returns("MyMethod"); + + // Act + string displayName = dataRow.GetDisplayName(methodInfoMock.Object, dataRow.Data); + + // Assert + Verify(displayName == "MyMethod ([[\"a\",\"b\",\"c\"],[\"d\",\"e\",\"f\"],[\"gh\",\"ij\",\"kl\"]],['m','n','o'],[[\"1\",\"2\",\"3\"],[\"4\",\"5\",\"6\"],[\"7\",\"8\",\"9\"]])"); + } + + private class DummyDataRowAttribute : DataRowAttribute + { public override string GetDisplayName(MethodInfo methodInfo, object[] data) => "Overridden DisplayName"; } } diff --git a/test/UnitTests/TestFramework.UnitTests/Attributes/DynamicDataAttributeTests.cs b/test/UnitTests/TestFramework.UnitTests/Attributes/DynamicDataAttributeTests.cs index 66abd3e4b7..771d74378b 100644 --- a/test/UnitTests/TestFramework.UnitTests/Attributes/DynamicDataAttributeTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Attributes/DynamicDataAttributeTests.cs @@ -113,7 +113,7 @@ void Action() public void GetDisplayNameShouldReturnDisplayName() { - object[] data = new object[] { 1, 2, 3 }; + object[] data = [1, 2, 3]; string displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); Verify("TestMethod1 (1,2,3)".SequenceEqual(displayName)); @@ -121,7 +121,7 @@ public void GetDisplayNameShouldReturnDisplayName() public void GetDisplayNameShouldReturnDisplayNameWithDynamicDataDisplayName() { - object[] data = new object[] { 1, 2, 3 }; + object[] data = [1, 2, 3]; _dynamicDataAttribute.DynamicDataDisplayName = "GetCustomDynamicDataDisplayName"; string displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); @@ -130,7 +130,7 @@ public void GetDisplayNameShouldReturnDisplayNameWithDynamicDataDisplayName() public void GetDisplayNameShouldReturnDisplayNameWithDynamicDataDisplayNameInDifferentClass() { - object[] data = new object[] { 1, 2, 3 }; + object[] data = [1, 2, 3]; _dynamicDataAttribute.DynamicDataDisplayName = "GetCustomDynamicDataDisplayName2"; _dynamicDataAttribute.DynamicDataDisplayNameDeclaringType = typeof(DummyTestClass2); @@ -142,10 +142,10 @@ public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodMi { void Action() { - object[] data = new object[] { 1, 2, 3 }; + object[] data = [1, 2, 3]; _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameWithMissingParameters"; - string displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); } Exception ex = VerifyThrows(Action); @@ -156,10 +156,10 @@ public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodIn { void Action() { - object[] data = new object[] { 1, 2, 3 }; + object[] data = [1, 2, 3]; _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameWithInvalidReturnType"; - string displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); } Exception ex = VerifyThrows(Action); @@ -170,10 +170,10 @@ public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodIn { void Action() { - object[] data = new object[] { 1, 2, 3 }; + object[] data = [1, 2, 3]; _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameWithInvalidFirstParameterType"; - string displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); } Exception ex = VerifyThrows(Action); @@ -184,10 +184,10 @@ public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodIn { void Action() { - object[] data = new object[] { 1, 2, 3 }; + object[] data = [1, 2, 3]; _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameWithInvalidSecondParameterType"; - string displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); } Exception ex = VerifyThrows(Action); @@ -198,10 +198,10 @@ public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodNo { void Action() { - object[] data = new object[] { 1, 2, 3 }; + object[] data = [1, 2, 3]; _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNameNonStatic"; - string displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); } Exception ex = VerifyThrows(Action); @@ -212,10 +212,10 @@ public void GetDisplayNameShouldThrowExceptionWithDynamicDataDisplayNameMethodPr { void Action() { - object[] data = new object[] { 1, 2, 3 }; + object[] data = [1, 2, 3]; _dynamicDataAttribute.DynamicDataDisplayName = "GetDynamicDataDisplayNamePrivate"; - string displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); } Exception ex = VerifyThrows(Action); @@ -226,10 +226,10 @@ public void GetDisplayNameShouldThrowExceptionWithMissingDynamicDataDisplayNameM { void Action() { - object[] data = new object[] { 1, 2, 3 }; + object[] data = [1, 2, 3]; _dynamicDataAttribute.DynamicDataDisplayName = "MissingCustomDynamicDataDisplayName"; - string displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); + _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); } Exception ex = VerifyThrows(Action); @@ -244,9 +244,9 @@ public void GetDisplayNameShouldReturnEmptyStringIfDataIsNull() public void GetDisplayNameHandlesNullValues() { - string[] data = new string[] { "value1", "value2", null }; - string[] data1 = new string[] { null, "value1", "value2" }; - string[] data2 = new string[] { "value1", null, "value2" }; + string[] data = ["value1", "value2", null]; + string[] data1 = [null, "value1", "value2"]; + string[] data2 = ["value1", null, "value2"]; string displayName = _dynamicDataAttribute.GetDisplayName(_testMethodInfo, data); Verify(displayName == "TestMethod1 (value1,value2,)"); @@ -263,30 +263,30 @@ public void DynamicDataSource_WithTuple_Works() { MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple)); var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithTuple), typeof(TestClassTupleData), DynamicDataSourceType.Property); - IEnumerable data = dynamicDataAttribute.GetData(testMethodInfo); + dynamicDataAttribute.GetData(testMethodInfo); dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithTuple), typeof(TestClassTupleData), DynamicDataSourceType.Method); - data = dynamicDataAttribute.GetData(testMethodInfo); + dynamicDataAttribute.GetData(testMethodInfo); } public void DynamicDataSource_WithValueTuple_Works() { MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple)); var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithValueTuple), typeof(TestClassTupleData), DynamicDataSourceType.Property); - IEnumerable data = dynamicDataAttribute.GetData(testMethodInfo); + dynamicDataAttribute.GetData(testMethodInfo); dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTuple), typeof(TestClassTupleData), DynamicDataSourceType.Method); - data = dynamicDataAttribute.GetData(testMethodInfo); + dynamicDataAttribute.GetData(testMethodInfo); } public void DynamicDataSource_WithValueTupleWithTupleSyntax_Works() { MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple)); var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithValueTupleWithTupleSyntax), typeof(TestClassTupleData), DynamicDataSourceType.Property); - IEnumerable data = dynamicDataAttribute.GetData(testMethodInfo); + dynamicDataAttribute.GetData(testMethodInfo); dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTupleWithTupleSyntax), typeof(TestClassTupleData), DynamicDataSourceType.Method); - data = dynamicDataAttribute.GetData(testMethodInfo); + dynamicDataAttribute.GetData(testMethodInfo); } #else public void DynamicDataSource_WithTuple_Throws() @@ -337,7 +337,7 @@ public class DummyTestClass /// /// Gets the reusable test data property. /// - public static IEnumerable ReusableTestDataProperty => new[] { new object[] { 1, 2, 3 }, [4, 5, 6] }; + public static IEnumerable ReusableTestDataProperty => [[1, 2, 3], [4, 5, 6]]; /// /// Gets the null test data property. @@ -361,7 +361,7 @@ public class DummyTestClass /// /// The . /// - public static IEnumerable ReusableTestDataMethod() => new[] { new object[] { 1, 2, 3 }, [4, 5, 6] }; + public static IEnumerable ReusableTestDataMethod() => [[1, 2, 3], [4, 5, 6]]; /// /// The custom display name method. @@ -510,7 +510,7 @@ public class DummyTestClass2 /// /// Gets the reusable test data property. /// - public static IEnumerable ReusableTestDataProperty2 => new[] { new object[] { 1, 2, 3 }, [4, 5, 6] }; + public static IEnumerable ReusableTestDataProperty2 => [[1, 2, 3], [4, 5, 6]]; /// /// The reusable test data method. @@ -518,7 +518,7 @@ public class DummyTestClass2 /// /// The . /// - public static IEnumerable ReusableTestDataMethod2() => new[] { new object[] { 1, 2, 3 }, [4, 5, 6] }; + public static IEnumerable ReusableTestDataMethod2() => [[1, 2, 3], [4, 5, 6]]; /// /// The custom display name method. diff --git a/test/UnitTests/TestFramework.UnitTests/Attributes/ExpectedExceptionAttributeTests.cs b/test/UnitTests/TestFramework.UnitTests/Attributes/ExpectedExceptionAttributeTests.cs index b997ee8449..c4c56feec2 100644 --- a/test/UnitTests/TestFramework.UnitTests/Attributes/ExpectedExceptionAttributeTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Attributes/ExpectedExceptionAttributeTests.cs @@ -70,6 +70,4 @@ public void GetExceptionMsgShouldReturnInnerExceptionMessageRecursivelyIfPresent /// /// Dummy class derived from Exception. /// -public class DummyTestClassDerivedFromException : Exception -{ -} +public class DummyTestClassDerivedFromException : Exception; diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/CommandLine.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/CommandLine.cs index c84a3a5b74..28db90800c 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/CommandLine.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/CommandLine.cs @@ -89,7 +89,9 @@ public async Task RunAsyncAndReturnExitCodeAsync( #if NET8_0_OR_GREATER await stopTheTimer.CancelAsync(); #else +#pragma warning disable VSTHRD103 // Call async methods when in an async method stopTheTimer.Cancel(); +#pragma warning restore VSTHRD103 // Call async methods when in an async method #endif return await exited; } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/DebuggerUtility.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/DebuggerUtility.cs index 6d391e8b8e..ddb5ed161b 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/DebuggerUtility.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/DebuggerUtility.cs @@ -156,7 +156,7 @@ private static bool AttachVs(Process vs, int pid, bool enableLog = false) } // Catch the exception if it is COMException coming directly, or coming from methodInvocation, otherwise just let it be. - catch (Exception ex) when (ex is COMException || (ex is TargetInvocationException tie && tie.InnerException is COMException)) + catch (Exception ex) when (ex is COMException or TargetInvocationException { InnerException: COMException }) { Trace($"ComException: Retrying in 250ms.\n{ex}", enabled: enableLog); Thread.Sleep(250); diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetCli.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetCli.cs index 11412ef0fd..f2dcd106e0 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetCli.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetCli.cs @@ -10,8 +10,8 @@ namespace Microsoft.Testing.TestInfrastructure; public static class DotnetCli { - private static readonly string[] CodeCoverageEnvironmentVariables = new[] -{ + private static readonly string[] CodeCoverageEnvironmentVariables = + [ "MicrosoftInstrumentationEngine_ConfigPath32_VanguardInstrumentationProfiler", "MicrosoftInstrumentationEngine_ConfigPath64_VanguardInstrumentationProfiler", "CORECLR_PROFILER_PATH_32", @@ -26,8 +26,8 @@ public static class DotnetCli "CODE_COVERAGE_PIPE_PATH", "MicrosoftInstrumentationEngine_LogLevel", "MicrosoftInstrumentationEngine_DisableCodeSignatureValidation", - "MicrosoftInstrumentationEngine_FileLogPath", -}; + "MicrosoftInstrumentationEngine_FileLogPath" + ]; private static int s_maxOutstandingCommand = Environment.ProcessorCount; private static SemaphoreSlim s_maxOutstandingCommands_semaphore = new(s_maxOutstandingCommand, s_maxOutstandingCommand); @@ -63,7 +63,8 @@ public static async Task RunAsync( foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) { // Skip all unwanted environment variables. - if (WellKnownEnvironmentVariables.ToSkipEnvironmentVariables.Contains(entry.Key!.ToString(), StringComparer.OrdinalIgnoreCase)) + string? key = entry.Key.ToString(); + if (WellKnownEnvironmentVariables.ToSkipEnvironmentVariables.Contains(key, StringComparer.OrdinalIgnoreCase)) { continue; } @@ -71,13 +72,13 @@ public static async Task RunAsync( if (disableCodeCoverage) { // Disable the code coverage during the build. - if (CodeCoverageEnvironmentVariables.Contains(entry.Key!.ToString(), StringComparer.OrdinalIgnoreCase)) + if (CodeCoverageEnvironmentVariables.Contains(key, StringComparer.OrdinalIgnoreCase)) { continue; } } - environmentVariables.Add(entry.Key!.ToString()!, entry.Value!.ToString()!); + environmentVariables.Add(key!, entry.Value!.ToString()!); } if (disableTelemetry) diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj b/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj index f1c0cdbae4..578038a762 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj @@ -1,9 +1,10 @@ - + $(MicrosoftTestingTargetFrameworks);netstandard2.0 enable enable + $(DefineConstants);SKIP_INTERMEDIATE_TARGET_FRAMEWORKS diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessFactory.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessFactory.cs index 31ec1c8ab8..0cda4179b8 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessFactory.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessFactory.cs @@ -58,7 +58,7 @@ public static IProcessHandle Start(ProcessConfiguration config, bool cleanDefaul ProcessHandleInfo processHandleInfo = new(); ProcessHandle processHandle = new(process, processHandleInfo); - process.Exited += (s, e) => config?.OnExit?.Invoke(processHandle, process.ExitCode); + process.Exited += (s, e) => config.OnExit?.Invoke(processHandle, process.ExitCode); if (config.OnStandardOutput != null) { diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProjectSystem.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProjectSystem.cs index 9144294525..00da9fa8f3 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProjectSystem.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProjectSystem.cs @@ -19,19 +19,19 @@ public class VSSolution : Folder private const string SlnGlobalSectionTemplate = @" Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0} - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {{C0047A98-3108-4928-8D43-FB6F8A49E3AB}} - EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0} + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {{C0047A98-3108-4928-8D43-FB6F8A49E3AB}} + EndGlobalSection EndGlobal "; @@ -57,7 +57,7 @@ public VSSolution(string? solutionFolder, string? solutionName) AddOrUpdateFileContent(_solutionFileName, MergeSolutionContent()); } - public ICollection Projects { get; private set; } = new List(); + public ICollection Projects { get; } = new List(); public string SolutionFile { get; private set; } @@ -73,10 +73,10 @@ public CSharpProject CreateCSharpProject(string projectName, params string[] tfm EndProject{4}", projectGuid, projectName, newProject.ProjectFile, configGuid, Environment.NewLine); _globals.AppendFormat(CultureInfo.InvariantCulture, @"{{{0}}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {{{0}}}.Debug|Any CPU.Build.0 = Debug|Any CPU{1}", configGuid, Environment.NewLine); + {{{0}}}.Debug|Any CPU.Build.0 = Debug|Any CPU{1}", configGuid, Environment.NewLine); _globals.AppendFormat(CultureInfo.InvariantCulture, @"{{{0}}}.Release|Any CPU.ActiveCfg = Release|Any CPU - {{{0}}}.Release|Any CPU.Build.0 = Release|Any CPU{1}", configGuid, Environment.NewLine); + {{{0}}}.Release|Any CPU.Build.0 = Release|Any CPU{1}", configGuid, Environment.NewLine); AddOrUpdateFileContent(_solutionFileName, MergeSolutionContent()); return newProject; diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/SlowestTestsConsumer.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/SlowestTestsConsumer.cs index 2581f27660..15a0fec88d 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/SlowestTestsConsumer.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/SlowestTestsConsumer.cs @@ -9,9 +9,9 @@ namespace Microsoft.Testing.TestInfrastructure; public sealed class SlowestTestsConsumer : IDataConsumer, ITestSessionLifetimeHandler { - private readonly List<(string TestId, double Milliseconds)> _testPerf = new(); + private readonly List<(string TestId, double Milliseconds)> _testPerf = []; - public Type[] DataTypesConsumed => new[] { typeof(TestNodeUpdateMessage) }; + public Type[] DataTypesConsumed => [typeof(TestNodeUpdateMessage)]; public string Uid => nameof(SlowestTestsConsumer); diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TargetFrameworks.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TargetFrameworks.cs index cb3918038b..d217e073ec 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TargetFrameworks.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TargetFrameworks.cs @@ -9,20 +9,24 @@ namespace Microsoft.Testing.TestInfrastructure; public static class TargetFrameworks { - public static TestArgumentsEntry[] Net { get; } = new TestArgumentsEntry[] - { + public static TestArgumentsEntry[] Net { get; } = + [ new("net8.0", "net8.0"), +#if !SKIP_INTERMEDIATE_TARGET_FRAMEWORKS new("net7.0", "net7.0"), new("net6.0", "net6.0"), - }; +#endif + ]; public static TestArgumentsEntry NetCurrent { get; } = Net[0]; - public static TestArgumentsEntry[] NetFramework { get; } = new TestArgumentsEntry[] { new("net462", "net462") }; + public static TestArgumentsEntry[] NetFramework { get; } = [new("net462", "net462")]; - public static TestArgumentsEntry[] All { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? Net.Concat(NetFramework).ToArray() - : Net; + public static TestArgumentsEntry[] All { get; } + = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? Net.Concat(NetFramework).ToArray() + : Net; - public static string ToMSBuildTargetFrameworks(this TestArgumentsEntry[] targetFrameworksEntries) => string.Join(";", targetFrameworksEntries.Select(x => x.Arguments)); + public static string ToMSBuildTargetFrameworks(this TestArgumentsEntry[] targetFrameworksEntries) + => string.Join(";", targetFrameworksEntries.Select(x => x.Arguments)); } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TempDirectory.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TempDirectory.cs index 105ae69130..70a41cc38a 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TempDirectory.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TempDirectory.cs @@ -272,7 +272,7 @@ internal static List ParseFiles(string fileContents) List lines = new(); foreach (string line in fileContents.Split('\n')) { - if (line.Trim()?.StartsWith("### ", StringComparison.InvariantCulture) ?? false) + if (line.Trim().StartsWith("### ", StringComparison.InvariantCulture)) { if (inFile) { diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestAsset.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestAsset.cs index 12b74b0863..b5f413c04a 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestAsset.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestAsset.cs @@ -61,7 +61,7 @@ private static (string Name, string Content) ParseFile(string fileContent) public static async Task GenerateAssetAsync(string assetName, string code, bool addDefaultNuGetConfigFile = true, bool addPublicFeeds = false) { TestAsset testAsset = new(assetName, addDefaultNuGetConfigFile ? string.Concat(code, GetNuGetConfig(addPublicFeeds)) : code); - string[] splitFiles = testAsset._assetCode.Split(new string[] { FileTag }, StringSplitOptions.RemoveEmptyEntries); + string[] splitFiles = testAsset._assetCode.Split([FileTag], StringSplitOptions.RemoveEmptyEntries); foreach (string fileContent in splitFiles) { (string, string) fileInfo = ParseFile(fileContent); diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHost.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHost.cs index 68359beaee..dc2733d28a 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHost.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHost.cs @@ -63,12 +63,13 @@ public async Task ExecuteAsync( foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) { // Skip all unwanted environment variables. - if (WellKnownEnvironmentVariables.ToSkipEnvironmentVariables.Contains(entry.Key!.ToString(), StringComparer.OrdinalIgnoreCase)) + string? key = entry.Key.ToString(); + if (WellKnownEnvironmentVariables.ToSkipEnvironmentVariables.Contains(key, StringComparer.OrdinalIgnoreCase)) { continue; } - environmentVariables.Add(entry.Key!.ToString()!, entry.Value!.ToString()!); + environmentVariables.Add(key!, entry.Value!.ToString()!); } // Define DOTNET_ROOT to point to the dotnet we install for this repository, to avoid diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestsRunWatchDog.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestsRunWatchDog.cs index ebbfddbbd8..f35320d659 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestsRunWatchDog.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestsRunWatchDog.cs @@ -34,8 +34,8 @@ public static async Task VerifyAsync(bool skip = false, bool fixBaseLine = false throw new InvalidOperationException("No tests were executed. Have you called 'TestsRunWatchDog.AddTestRun'?"); } - List expectedTestsDidNotRun = new(); - List unexpectedRanTests = new(); + List expectedTestsDidNotRun = []; + List unexpectedRanTests = []; using (FileStream fs = File.OpenRead(BaselineFile)) { using StreamReader streamReader = new(fs); @@ -107,9 +107,9 @@ public static async Task VerifyAsync(bool skip = false, bool fixBaseLine = false { if (fixBaseLine) { - List tests = new(File.ReadAllLines(BaselineFile)); - tests.RemoveAll(t => expectedTestsDidNotRun.Contains(t)); - tests.AddRange(unexpectedRanTests ?? new List()); + List tests = [.. File.ReadAllLines(BaselineFile)]; + tests.RemoveAll(expectedTestsDidNotRun.Contains); + tests.AddRange(unexpectedRanTests); tests.Sort(); File.WriteAllLines(BaselineFile, tests); Console.WriteLine(); diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/WellKnownEnvironmentVariables.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/WellKnownEnvironmentVariables.cs index 3d4f6587e4..98ed7cc933 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/WellKnownEnvironmentVariables.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/WellKnownEnvironmentVariables.cs @@ -5,8 +5,8 @@ namespace Microsoft.Testing.TestInfrastructure; public static class WellKnownEnvironmentVariables { - public static readonly string[] ToSkipEnvironmentVariables = new[] - { + public static readonly string[] ToSkipEnvironmentVariables = + [ // Skip dotnet root, we redefine it below. "DOTNET_ROOT", @@ -40,6 +40,6 @@ public static class WellKnownEnvironmentVariables "TESTINGPLATFORM_DIAGNOSTIC", // Isolate from the skip banner in case of parent, children tests - "TESTINGPLATFORM_CONSOLEOUTPUTDEVICE_SKIP_BANNER", - }; + "TESTINGPLATFORM_CONSOLEOUTPUTDEVICE_SKIP_BANNER" + ]; } diff --git a/test/Utilities/TestFramework.ForTestingMSTest/AdapterToTestPlatform.cs b/test/Utilities/TestFramework.ForTestingMSTest/AdapterToTestPlatform.cs index 30c318da01..8064fad1b4 100644 --- a/test/Utilities/TestFramework.ForTestingMSTest/AdapterToTestPlatform.cs +++ b/test/Utilities/TestFramework.ForTestingMSTest/AdapterToTestPlatform.cs @@ -164,8 +164,7 @@ private static IEnumerable DiscoverTests(IEnumerable? assembli LogMessage(logger, TestMessageLevel.Informational, $"Discovering tests in assembly '{assemblyName}'"); var assembly = Assembly.LoadFrom(assemblyName); - IEnumerable assemblyTestContainerTypes = assembly.DefinedTypes.Where(typeInfo => - IsTestContainer(typeInfo)); + IEnumerable assemblyTestContainerTypes = assembly.DefinedTypes.Where(IsTestContainer); // TODO: Fail if no container? foreach (TypeInfo? testContainerType in assemblyTestContainerTypes) @@ -203,9 +202,7 @@ private static bool TryFindMethodsToRun(TestCase testCase, IMessageLogger? logge testCase.FullyQualifiedName.StartsWith(typeInfo.FullName, StringComparison.Ordinal)); // Is it better to use Activator.CreateInstance? - setupMethod = testContainerType.DeclaredConstructors.Single(ctorInfo => - ctorInfo.IsPublic - && ctorInfo.GetParameters().Length == 0); + setupMethod = testContainerType.GetConstructor([]); teardownMethod = testContainerType.BaseType.GetMethod("Dispose"); TypeInfo type = testContainerType; testMethod = testContainerType.DeclaredMethods.Single(methodInfo => diff --git a/test/Utilities/TestFramework.ForTestingMSTest/CallerArgumentExpressionAttribute.cs b/test/Utilities/TestFramework.ForTestingMSTest/CallerArgumentExpressionAttribute.cs index ab42f3ac8d..2ca70c0553 100644 --- a/test/Utilities/TestFramework.ForTestingMSTest/CallerArgumentExpressionAttribute.cs +++ b/test/Utilities/TestFramework.ForTestingMSTest/CallerArgumentExpressionAttribute.cs @@ -7,7 +7,7 @@ namespace System.Runtime.CompilerServices; /// /// Allows capturing of the expressions passed to a method. /// -[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Parameter)] internal sealed class CallerArgumentExpressionAttribute : Attribute { /// diff --git a/test/Utilities/TestFramework.ForTestingMSTest/CodeAnalysisAttributes.cs b/test/Utilities/TestFramework.ForTestingMSTest/CodeAnalysisAttributes.cs index ca5c197d1f..42657395bd 100644 --- a/test/Utilities/TestFramework.ForTestingMSTest/CodeAnalysisAttributes.cs +++ b/test/Utilities/TestFramework.ForTestingMSTest/CodeAnalysisAttributes.cs @@ -6,12 +6,10 @@ namespace System.Diagnostics.CodeAnalysis; /// Applied to a method that will never return under any circumstance. [AttributeUsage(AttributeTargets.Method, Inherited = false)] -internal sealed class DoesNotReturnAttribute : Attribute -{ -} +internal sealed class DoesNotReturnAttribute : Attribute; /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. -[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +[AttributeUsage(AttributeTargets.Parameter)] internal sealed class NotNullWhenAttribute : Attribute { /// diff --git a/test/Utilities/TestFramework.ForTestingMSTest/DoesNotReturnIfAttribute.cs b/test/Utilities/TestFramework.ForTestingMSTest/DoesNotReturnIfAttribute.cs new file mode 100644 index 0000000000..d4138e4020 --- /dev/null +++ b/test/Utilities/TestFramework.ForTestingMSTest/DoesNotReturnIfAttribute.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if NETSTANDARD2_0 || NETFRAMEWORK + +#pragma warning disable SA1623 // Remove ridiculous stylecop documentation warning +namespace System.Diagnostics.CodeAnalysis; + +/// +/// Specifies that the method will not return if the associated +/// parameter is passed the specified value. +/// +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +[AttributeUsage(AttributeTargets.Parameter)] +public sealed class DoesNotReturnIfAttribute : + Attribute +{ + /// + /// Gets the condition parameter value. + /// Code after the method is considered unreachable by diagnostics if the argument + /// to the associated parameter matches this value. + /// + public bool ParameterValue { get; } + + /// + /// Initializes a new instance of the + /// class with the specified parameter value. + /// + /// + /// The condition parameter value. + /// Code after the method is considered unreachable by diagnostics if the argument + /// to the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => + ParameterValue = parameterValue; +} + +#endif diff --git a/test/Utilities/TestFramework.ForTestingMSTest/TestContainer.cs b/test/Utilities/TestFramework.ForTestingMSTest/TestContainer.cs index 4b2cedbb41..7cf3f24cff 100644 --- a/test/Utilities/TestFramework.ForTestingMSTest/TestContainer.cs +++ b/test/Utilities/TestFramework.ForTestingMSTest/TestContainer.cs @@ -51,7 +51,7 @@ public void Dispose() } public static void Verify( - bool condition, + [DoesNotReturnIf(false)] bool condition, [CallerArgumentExpression(nameof(condition))] string? expression = default, [CallerMemberName] string? caller = default, [CallerFilePath] string? filePath = default, diff --git a/eng/coverage.config b/test/coverage.config similarity index 63% rename from eng/coverage.config rename to test/coverage.config index 1e3ac0310b..fd0d43c7e7 100644 --- a/eng/coverage.config +++ b/test/coverage.config @@ -3,7 +3,14 @@ + .*Microsoft\.Testing\.Extensions\.CrashDump\.dll$ + .*Microsoft\.Testing\.Extensions\.HangDump\.dll$ + .*Microsoft\.Testing\.Extensions\.Telemetry\.dll$ + .*Microsoft\.Testing\.Extensions\.TrxReport\.dll$ + .*Microsoft\.Testing\.Extensions\.TrxReport\.Abstractions\.dll$ + .*Microsoft\.Testing\.Extensions\.VSTestBridge\.dll$ .*Microsoft\.Testing\.Platform\.dll$ + .*Microsoft\.Testing\.Platform\.MSBuild\.dll$ .*Microsoft\.VisualStudio\.TestPlatform\.MSTest\.TestAdapter\.dll$ .*Microsoft\.VisualStudio\.TestPlatform\.MSTestAdapter\.PlatformServices\.dll$ .*Microsoft\.VisualStudio\.TestPlatform\.TestFramework\.dll$ @@ -18,8 +25,10 @@ .*\\runtimes\\win\\native\\.* - False + False False False + False + True